#Event handlers + loops + signals

1 messages · Page 1 of 1 (latest)

inner depot
#

Hi, I've two questions on the following minimal example where I want to invoke a callback for each item a Vec signal.

  1. As soon as I use *e in the callback, I get a compile error 'elems' does not live long enough on the loop. I can resolve this by doing for e in elems() instead but do I understand correctly that this would cause a clone the vec on each render? The other way around this is to make all callbacks take the loop index instead – is that the idiomatic approach?

  2. I need to make remove mut to be able to use it in the loop. I don't really understand why as the lambda itself should never change? In the example here it is not an issue yet, but it becomes one once I want to nest lambdas like onclick: move |_| run_and_log("rm elem", || remove_cell(i)) as the captured remove_cell becomes non-mutable again. Is there a better way to do this?

    let mut elems = use_signal(|| vec![100, 101, 102]);

    let mut remove = move |id| {
        elems.write().retain(|&x| x != id);
    };

    rsx! {
        for e in elems.iter() {
            div {
                "elem {e}"
                button {
                    onclick: move |_| remove(*e),
                    "remove"
                }
            }
        }
    }
odd heart
#

make it a function instead of closure

    let mut elems = use_signal(|| vec![100, 101, 102]);

    fn remove(id: i64, elems: &mut Signal<Vec<i64>>) {
        elems.write().retain(|&x| x != id);
    }

    rsx! {
        for e in elems() {
            div {
                "elem {e}"
                button { onclick: move |_| remove(e, &mut elems), "remove" }
            }
        }
    }
#

closures are quite fragile in Rust

inner depot
#

Mh, that gets rid of the mut error for remove itself but it just shifts the error since now cells cannot be mutably borrowed when passed into run_and_log

#

But passing the signal by value works fine. And more fundamentally my error was in run_and_log, which needs to take a FnMut instead of Fn.

inner depot
#

So question (2) is obsolete, leaves only (1). The approach with elems() doesn't feel quite right if I scale this to a more practical example where elem is a heavier struct with an id field that is not/should not be Clone. But this seems to work while still being readable and not relying on using indices:

    let mut elems = use_signal(|| vec![MyStruct{id: 100}, MyStruct{id: 200}]);

    let remove = |id| {
        move |_| e.write().retain(|x| x.id != id)
    };

    rsx! {
        for e in elems.iter() {
            div {
                "elem {e.id}"
                button {
                    onclick: remove(e.id)
                    "remove"
                }
            }
        }
    }

But still curious about other possible approaches as this might not always be concise either with more nested structs.

versed cargo
#

If your struct is cheap to copy, the clone or copy by index approach is good

#

You could also write it like this:

    let mut elems = use_signal(|| vec![MyStruct{id: 100}, MyStruct{id: 200}]);

    rsx! {
        for e in elems.iter().copied() {
            div {
                "elem {e.id}"
                button {
                    onclick: move |_| e.write().retain(|x| x.id != e.id)
                    "remove"
                }
            }
        }
    }
#

If you absolutely can't clone any elements, you can map the signal with the map function: https://docs.rs/dioxus-signals/0.5.1/dioxus_signals/trait.Readable.html#method.map

    let mut elems = use_signal(|| vec![MyStruct{id: 100}, MyStruct{id: 200}]);

    rsx! {
        for e in (0..elems.len()).map(|index| elems.map(move |array| &array[index])) {
            div {
                "elem {e().id}"
                button {
                    onclick: move |_| e.write().retain(|x| x.id != e().id)
                    "remove"
                }
            }
        }
    }
odd heart
#

I wouldn't worry too much about copying or cloning, make it work and easy to understand, worry about optimization if truly necessary based on benchmarks

inner depot
#

yes, for sure – trying to build an understanding of how it would be handled once necessary mostly

#

elems.iter().copied() does not seem to work however. Just that in isolation fails with "expected ReadableValueIterator<'_, Signal<Vec<MyStruct>>> to be an iterator that yields &_, but it yields GenerationalRef<Ref<'_, _>>"

versed cargo
#

Ah, yeah elems.iter().map(|el| *el) does the same thing