Executing JS from Dioxus doesn't yield the same result as from browser console.
Problem
Executing java-script through dioxus doesn't produce the correct expected result.
I am trying to do the following:
// Dioxus component
// ...
let oninput = move |e: Event<FormData>| {
let res = execute_js(&cx, "var ot=window.getSelection().baseNode.parentElement.offsetTop; dioxus.send(ot); console.log(ot);");
println!("{:?}", res);
println!("oninput data value: {:?}", e.data.value);
};
This JS code should return an offset from the top of an element.
Steps To Reproduce
Expected behavior
Both results from dioxus.send(...) and the browser console should match.
Preview
Environment:
- Dioxus version:
0.4.1 - Rust version:
1.76.0 - OS info:
Window 10 - App platform:
desktop
Questionnaire
- [x] I'm interested in fixing this myself but don't know where to start
- [ ] I would like to fix and I have a solution
- [ ] I don't have time to fix this right now, but maybe later
Issue was from my side not calling .restart(...) on the use_future hook. However, I still don't understand why the first value is always None on Dioxus side
What is execute_js?
A small wrapper to run js and retrieve the value
// handy utility method to execute js
pub fn execute_js<'a, T>(cx: &'a Scope<'a, T>, js_code: &str) -> Option<&'a Value> {
// Use eval returns a function that can spawn eval instances
let create_eval = use_eval(cx);
// You can create as many eval instances as you want
let eval = create_eval(js_code).unwrap();
let future = use_future(cx, (), |_| {
to_owned![eval];
async move {
// You can receive any message from JavaScript with the recv method
eval.recv().await.unwrap()
}
});
future.restart();
future.value()
}
You can't use hooks inside of event handlers, or any code that runs conditionally. It violates the rules of hooks
What's the recommended way of having an entity which you can throw some js commands to it and get the result?
I tried the following as well:
pub fn TArea(cx: Scope<TAreaProps>) -> Element {
// .. Component
let create_eval = use_eval(cx);
let eval = use_state(cx, || create_eval("").unwrap());
let future = use_future(cx, (), |_| {
to_owned![eval];
async move {
eval.recv().await.unwrap()
}
});
let oninput = move |e: Event<FormData>| {
eval.set(create_eval(GET_INITIAL_EDITABLE_AREA_OFFSET_TOP_LEFT).unwrap());
future.restart();
let res = future.value();
println!("{:?}", res);
}
// ... rest
This gives a similar behaviour with the first value being always None on Dioxus side
calling .value() on a future will get the current value of the future. I think in 0.4.1, futures are not polled until the end of the render, so it will at least resolve to None once.
If you want to get a value immediately without a future, you can manually create a function with js-sys. The eval API in dioxus is cross platform and some platforms require all results to be async
But js-sys isn't going to work with the desktop platforms, right?
Exactly, if you need to target anything more than web, the API needs to be async which means the use_future will be None before it is resolved