6

This question is written for Yew v0.19

Asynchronous foreign JavaScript functions can be used in Rust through Closures, as the function to pass-in:


#[wasm_bindgen]
extern "C" {
    fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
}

// ...

let cb = Closure::new(|| {
    log("interval elapsed!");
});

let interval_id = setInterval(&cb, 1_000);

This is nice for a pedantic examples, but Closures have a critical requirement - the function applied needs to have a 'static lifetime. Likewise, with Yew applications, a perfect mechanism for spontaneous response is the Message enum, and having it update() the Model. However, the link() mechanism in Context (which issues messages) does not have a static lifetime.

In an ideal world, the value submitted to the closure could just be applied as a Yew component message:

struct Model {
    thing: Option<JsValue>,
}

enum Msg {
    GotThing(JsValue),
}

#[wasm_bindgen]
extern "C" {
    fn createThing(closure: &Closure<dyn FnMut(JsValue) -> ());
}

impl Component for Model {
    type Message = Msg;
    type Properties = ();

    fn create(_ctx: &Context<Self>) -> Self {
        Model {
            thing: None, // initial value
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::GotThing(x) => { // handle the message
                self.thing = Some(x);
                true
            },
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        if first_render {
            let cb: Box<dyn FnMut(JsValue) -> ()> = Box::new(|x| {
                // try and issue the message
                ctx.link().send_message(Msg::GotThing(x));
                // ^ doesn't have a `'static` lifetime! Won't compile
            });
            createThing(Closure::wrap(&cb));
        }
    }

    // fn view() ... omitted, not relevant
}

I'm wondering if there's a way to convert a Callback into a Closure, or if there's a better, more canonical way to do this, to please correct me.

Another idea I had would use some kind of queue defined statically (which wouldn't be safe as it's a mutable static variable), but then it could be used as an intermediary data type between the Closure passed to createThing, and messages could be dispatched within the component.

Maybe there's an external way to interact with a Yew component that I'm overlooking? I'm not sure how to resolve this issue. What would be the most correct way to achieve this goal?

Athan Clark
  • 3,886
  • 2
  • 21
  • 39
  • Excellent question - I currently struggle with the same problem. Did you ever figure this out? – SirVer Oct 24 '22 at 20:04
  • This could help me [solve a similar problem](https://stackoverflow.com/questions/75203156) - calling a Yew callback from JS to change the Yew route. – jq170727 Jan 22 '23 at 20:28
  • Looks like [this gist](https://gist.github.com/craftyc0der/1e71964cc3e85d533f0f70985c22a74a#setup-wasm-bindgen-for-the-javascript-code) from [craftyc0der](https://gist.github.com/craftyc0der) may provide one way of doing this. – jq170727 Jan 22 '23 at 20:44

0 Answers0