DioxusLabs / DioxusLabs/dioxus

ReadSignal coercion can cause extra reruns

Open
#5,474 0 comments 0 reactions 0 assignees View on GitHub
bug signals
Dominant language
Rust
Stars
39.1k
Forks
1.9k
Avg merge
4d 10h
Merged PRs (30d)
4

Description

**Problem**

When read signal swaps in place, it transfers more subscribers than it should which can cause scopes that subscribe to the old signal before coercion to rerun when the new signal changes

**Steps To Reproduce**

Run this example and follow the steps in the description:
```rust
#![allow(non_snake_case)]

use dioxus::prelude::*;

fn main() {
dioxus::launch(app);
}

fn app() -> Element {
let mut use_b = use_signal(|| false);
let mut effect_runs = use_signal(|| 0);
let mut signal_a = use_signal(|| 0);
let mut signal_b = use_signal(|| 0);

use_effect(move || {
let value = signal_a();
let runs = {
let mut write = effect_runs.write();
*write += 1;
*write
};
println!("parent effect ran: A = {value}, runs = {runs}");
});

let child_signal = if use_b() { signal_b } else { signal_a };

rsx! {
div {
max_width: "52rem",
margin: "0 auto",
padding: "2rem",
font_family: "sans-serif",

h1 { "Mapped ReadSignal point_to repro" }
p {
"Steps: leave A and B equal, swap the child from A to B, then increment B. "
"The parent effect below only reads A, so its run count should not change when B changes."
}

div {
display: "flex",
gap: "0.75rem",
flex_wrap: "wrap",
margin_bottom: "1rem",

button {
onclick: move |_| use_b.set(false),
"Child uses A"
}
button {
onclick: move |_| use_b.set(true),
"Child uses B"
}
button {
onclick: move |_| signal_a += 1,
"Increment A"
}
button {
onclick: move |_| signal_b += 1,
"Increment B"
}
button {
onclick: move |_| {
signal_a.set(0);
signal_b.set(0);
use_b.set(false);
effect_runs.set(0);
},
"Reset"
}
}

ul {
li { "Parent effect runs: {effect_runs}" }
li { "A: {signal_a}" }
li { "B: {signal_b}" }
li {
"Child source: "
if use_b() { "B" } else { "A" }
}
}

Child { sig: child_signal }
}
}
}

#[component]
fn Child(sig: ReadSignal) -> Element {
rsx! {
div {
margin_top: "1rem",
padding: "1rem",
border: "1px solid #ccc",
border_radius: "8px",
"Child value: {sig}"
}
}
}
```

**Expected behavior**

The effect should only rerun when `signal_a` changes. I think we can fix this issue by keeping a separate layer of subscribers for readsignal and only moving those subscribers instead of the whole list

**Environment:**

- Dioxus version: main
- Rust version: nightly
- OS info: macos
- App platform: all

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.