Why does cloning a Leptos signal result in updating the same value multiple times?

453 Views Asked by At

I am working on a simple application using the Leptos framework which updates a counter. I have cloned a ReadSignal and its corresponding setter function using .clone(). However, I noticed that when I update the original and the cloned signals, they both increment the same value.

#[component]
fn App(cx: Scope) -> impl IntoView {
    let (count, set_count) = create_signal(cx, 0);
    let dup_count = count.clone();
    let dup_set = set_count.clone();
    view! { cx,
        <div>
            <button
                on:click=move |_| {
                    set_count.update(|n| *n += 1);
                    dup_set.update(|n| *n += 2);
                }

            >
                "Click me: "
                {count.get()}
            </button>
            <div>{count}</div>
            <div>{dup_count}</div>
            <ProgressBar progress=count />
            <ProgressBar progress=dup_count />
       </div>
    }
}

I am trying to better understand Signals and the different methods in Leptos. I would love some explanations to deepen my understanding. Thank you in advance!

3

There are 3 best solutions below

0
On

The solution to your problem might be adding move ||:

<button
  on:click=move |_| {
    set_count.update(|n| *n += 1);
    dup_set.update(|n| *n += 2);
  }
  "Click me: " {move || count.get()}
</button>

See https://leptos-rs.github.io/leptos/view/01_basic_component.html where this is explained.

0
On

Because of how signals are constructed in leptos, cloning a signal does not simply clone its existence from scratch. It instead creates a "reference" to the parent signal. As stated in another answer this can be easily avoided by constructing another signal separately.

let (apple, set_apple) = create_signal(String::new());
let setapple_clone = set_apple.clone();

setapple_clone("from clone".to_string()); // setapple_clone practically inherits the functionality of set_apple as if it was only copied
println!("{}",apple()); // "from clone"

set_apple("from parent".to_string());
println!("{}",apple()); // "from parent"

Note that this example uses nightly rust so no .get() type notation for the signals

0
On

Signals work in a similar way to channels. A pair of WriteSignal and ReadSignal are always connected to each other, as are their clones. This means that you can update the same signal from multiple places by cloning a WriteSignal and react to the same signal from multiple places by cloning a ReadSignal.

If you want to make a new WriteSignal which is not connected to the original ReadSignal then you need to create it from scratch with create_signal.