dioxus icon indicating copy to clipboard operation
dioxus copied to clipboard

UseEffect only runs on render

Open elias098 opened this issue 10 months ago • 2 comments

Problem

Currently use_effect uses wait_for_next_render().await; which means that it only runs on renders.

This is a problem if a signal gets updates without triggering a render.

One such instance is that a queue of updates build up, with one effect running on every render, as below.

Steps To Reproduce

fn App() -> Element {
    let mut signal = use_signal(|| 1);
    use_effect(move || log::log!(log::Level::Info, "{}", signal()));

    let mut eval = use_hook(|| {
        eval(
            r#"
            setInterval(() => {
                console.log("update js");
                dioxus.send("");
            }, 1000)
            "#,
        )
    });

    use_hook(|| {
        spawn(async move {
            loop {
                eval.recv().await.unwrap();
                signal += 1;
            }
        })
    });

    rsx! {
        button {
            onclick: |_| ()
        }
    }
}

Expected behavior

I would expect effects to run any time a used signal updates

Environment:

  • Dioxus version: 0.5.1
  • Rust version: 1.76.0
  • OS info: MacOS
  • App platform: web

Questionnaire

  • [ ] 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

elias098 avatar Apr 13 '24 21:04 elias098

If this is intended it should probably be documented. this might also just be me being used to leptos and not understanding dioxus version properly.

elias098 avatar Apr 13 '24 22:04 elias098

Looks like signals mutated from async tasks do not rerun effects

Doesn't work:

fn app() -> Element {
    let mut count = use_signal(|| 0);

    use_effect(move || {
        println!("{}", count());
    });

    use_hook(move || {
        spawn(async move {
            loop {
                sleep(Duration::from_secs(1)).await;
                count += 1
            }
        })
    });

    rsx!(
        button {
            onclick: move |_| println!("{}", count()),
            "read"
        }
        button {
            onclick: |_| needs_update(),
            "rerun"
        }
    )
}

Works:

fn app() -> Element {
    let mut count = use_signal(|| 0);

    use_effect(move || {
        println!("{}", count);
    });

    println!("rerunning");

    rsx!(
        button {
            onclick: move |_| count += 1,
            "Increase"
        }
        button {
            onclick: |_| needs_update(),
            "rerun"
        }
    )
}

marc2332 avatar Apr 14 '24 07:04 marc2332

I think this should be fixed with #2370, but GitHub didn't automatically close the issue on merge since the keyword references two issues.

pablosichert avatar May 14 '24 14:05 pablosichert