DioxusLabs / dioxus

Fullstack GUI library for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
18.5k stars 704 forks source link

`use_effect` not running when writing to signal asynchronously #2347

Open pablosichert opened 2 weeks ago

pablosichert commented 2 weeks ago

Heyo,

thank you for this great library! I'm having a problem with signals that I reduced to a minimal example.

I want to run a side effect within a use_effect that is derived from a signal. The signal itself is not used for rendering.

When writing to this signal asynchronously, I can not get it to fire consistently (it works sometimes, but very rarely).

Steps To Reproduce

Cargo.toml ```toml [package] name = "signal_asynchronous_write_not_rerendering" version = "0.0.0" edition = "2021" [dependencies] dioxus = { version = "0.5", features = ["fullstack"] } log = "0.4" dioxus-logger = "0.4" wasm-bindgen-futures = "0.4" web-sys = "0.3" [features] default = [] server = ["dioxus/axum"] web = ["dioxus/web"] ```
main.rsExpand to see full file ```rust use dioxus::prelude::*; use log::LevelFilter; use wasm_bindgen_futures::JsFuture; use web_sys::{ js_sys::{Function, Promise}, window, }; fn main() { dioxus_logger::init(LevelFilter::Debug).unwrap(); launch(App); } pub async fn sleep(delay: i32) { let mut callback = |resolve: Function, _reject: Function| { window() .unwrap() .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, delay) .unwrap(); }; let promise = Promise::new(&mut callback); JsFuture::from(promise).await.unwrap(); } /// ... ```
/// ...

fn use_delayed_double(x: Signal<usize>) -> Signal<usize> {
    let mut foo = use_signal(|| x());

    use_memo(move || {
        let y = x * 2;

        spawn(async move {
            log::debug!("Sleeping...");
            sleep(100).await;
            log::debug!("Writing signal");
            *foo.write() = y; // <<---- Writing to the signal.
        });
    });

    foo
}

#[allow(non_snake_case)]
fn App() -> Element {
    let mut x = use_signal(|| 123);
    let double = use_delayed_double(x);

    use_effect(move || {
        let double = double();
        log::debug!("double: {:?}", double); // <<---- Expecting the side effect to be executed.
    });

    rsx! {
        button {
            onclick: move |_| x += 1,
            "increment"
        },
        "x: {x}"
        // "double: {double}" // <<---- When commenting this line in, logging "double" works as expected.
    }
}

Launch app using dx serve.

output

[DEBUG] signal_asynchronous_write_not_rerendering - double: 123
[DEBUG] signal_asynchronous_write_not_rerendering - Sleeping...
[DEBUG] signal_asynchronous_write_not_rerendering - Writing signal

Expected behavior

output

[DEBUG] signal_asynchronous_write_not_rerendering - double: 123
[DEBUG] signal_asynchronous_write_not_rerendering - Sleeping...
[DEBUG] signal_asynchronous_write_not_rerendering - Writing signal
[DEBUG] signal_asynchronous_write_not_rerendering - double: 246 <<----

Environment:

Questionnaire

pablosichert commented 1 week ago

This seems to share the underlying issue with #2307.