#How to call an EventHandler from a Javascript closure?

1 messages · Page 1 of 1 (latest)

shut sonnet
#

I am trying to wrap CodeMirror as a Dioxus component. I am able to get a wasm_bindgen::Closure called whenever there's a change. I'd like to call an EventHandler when that happens. However, calling directly gives a warning that the a writing to a signal happened at render time. I tried to wrap the call to EventHandler in a use_effect but this leads to a panic:

let closure = Closure::wrap(Box::new(move |doc: JsValue| {
  use_effect(move || {
      on_change.call(doc.as_string().unwrap());
  });
}) as Box<dyn FnMut(JsValue)>);         
webapp.js:481 panicked at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/dioxus-core-0.5.6/src/global_context.rs:158:64:
to be in a dioxus runtime
rotund bear
#

You can get the current runtime from the component with Runtime::current().unwrap() and then create the runtime gaurd inside the wasm bindgen closure

#

use_effect(move || {
on_change.call(doc.as_string().unwrap());
});
You also cannot call hooks outside of the body of a component

shut sonnet
#

Thanks Evan for the answer, however I wasn't able to get it to work. It still panics when the event is triggered. Here is the code I have:

#[derive(Props, Clone, PartialEq)]
struct CodeMirrorProps {
    // runtime: Rc<Runtime>,
    id: &'static str,
    value: String,
    on_change: EventHandler<String>,
}

fn CodeMirrorEditor(cx: CodeMirrorProps) -> Element {
    use_effect(move || {
        let runtime = Runtime::current().unwrap();
        let elem = web_sys::window()
            .unwrap()
            .document()
            .unwrap()
            .get_element_by_id(cx.id)
            .unwrap()
            .dyn_into::<web_sys::HtmlElement>()
            .unwrap();

        let closure = Closure::wrap(Box::new(move |doc: JsValue| {
            let _guard = RuntimeGuard::new(runtime.clone());
            use_effect(move || {
                cx.on_change.call(doc.as_string().unwrap());
            });
        }) as Box<dyn FnMut(JsValue)>);
        makeEditor(&elem, &cx.value, closure.as_ref().unchecked_ref());
        closure.forget();
    });
    rsx! {
        div {
            id: "{cx.id}",
        }
    }
}

The runtime is outside of my props in my code as I prefer not to expose it as the component APIs (do I have to?). I have the _guard inside the closure as you wrote, but not inside the use_effect - is that the right place? Inside the use_effect it doesn't compile since it turns the parameter of use_effect into FnOnce.

rotund bear
#

That should work for the closure, but use_effect can't be called inside the closure

#

It violates the rules of hooks