Panick could not set stored value
When I set a new list of items for my AnimatedFor list signal, I get the following panick
I was unable to reproduce it. The panic looks as if the AnimatedFor got dissolved along with the previous list, but that's a very wild speculation on my side. Could you provide me with a code that recreates such behavior?
So it seems to be happening when just setting an empty Vec to the items of the AnimatedFor component Do you know where I could look to find out more. I am getting NodeRef warnings as well but it is hard to trace back because of how the WASM trace logs appear in the console
<AnimatedFor appear=true each=move || trains.get() enter_class="lineUp" leave_class="fadeOut1500" move_class="transition duration-1000 delay2" key=|t| (t.trein_id.clone() + &t.stops[0].station.lange_naam) children=move |s| { view! { <TrainItem train=s tracks=station_tracks/> } } />
Signal setup code:
let (trains, set_trains) = create_signal::<Vec<Train>>(vec![]);
I had no luck with reproducing the problem. I've modified this file of my CSR example with the code:
mod button;
mod circle;
use leptos::{
component,
create_signal,
mount_to_body,
view,
Callback,
IntoView,
SignalUpdate,
StoredValue,
View,
};
use leptos_animated_for::AnimatedFor;
use rand::{thread_rng, Rng};
use crate::{button::Button, circle::Circle};
#[derive(Clone)]
struct Item {
id: usize,
view: View,
}
#[component]
fn App() -> impl IntoView {
let last_added_item_id = StoredValue::new(0);
let rng = StoredValue::new(thread_rng());
let create_new_item = Callback::new(move |()| {
let id = last_added_item_id();
last_added_item_id.set_value(id + 1);
let mut circle = None;
let mut bg_color_class = None;
rng.update_value(|rng| {
circle = Some(matches!(rng.gen_range(0..=1), 0));
bg_color_class = Some(match rng.gen_range(0..=2) {
0 => "bg-red-500/70",
1 => "bg-green-500/70",
_ => "bg-blue-500/70",
});
});
let view = if circle.unwrap() {
view! { <Circle class=bg_color_class.unwrap()>{id}</Circle> }
.into_view()
} else {
view! {
<div class=["rounded flex items-center justify-center", bg_color_class.unwrap()]
.join(" ")>{id}</div>
}
.into_view()
};
Item { id, view }
});
let (items, set_items) = create_signal(Vec::new());
let add_random = move |_| {
set_items.update(|items| {
let item = create_new_item(());
rng.update_value(|rng| {
items.insert(rng.gen_range(0..(items.len() + 1)), item);
});
});
};
let remove_random = move |_| {
set_items.update(|items| {
if items.is_empty() {
return;
}
rng.update_value(|rng| {
items.remove(rng.gen_range(0..items.len()));
});
});
};
let clear = move |_| {
set_items(Vec::new());
};
view! {
<div class="min-h-screen py-8 bg-gray-800 font-bold text-white">
<div class="flex flex-wrap gap-2 justify-center">
<Button on:click=add_random>{"Add random"}</Button>
<Button on:click=remove_random>{"Remove random"}</Button>
<Button on:click=clear>{"Clear"}</Button>
</div>
<div class="mt-4 grid grid-cols-[repeat(5,auto)] gap-2 justify-center text-xl [&>*]:w-16 [&>*]:h-16">
<AnimatedFor
each=items
key=|item| item.id
children=|item| item.view
enter_from_class="opacity-0"
enter_class="animate-fade-in-bottom-left"
move_class="duration-1000"
leave_class="opacity-0 duration-1000"
appear=true
/>
</div>
</div>
}
}
fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(App);
}
and added some dependencies for debugging in Cargo.toml:
[package]
name = "csr-example"
version = "0.0.0"
edition = "2021"
[dependencies]
getrandom = { version = "0.2.12", features = ["js"], optional = true }
leptos = { version = "0.6.9", features = ["csr", "nightly"] }
leptos_animated_for = { path = "../.." }
rand = "0.8.5"
console_error_panic_hook = "0.1.7"
console_log = "1"
log = "0.4.20"
Could you please try running it and modifing it further so that the app panics in the way you described? That would allow me to pinpoint the exact cause of the problem.
The code you've provided doesn't tell me much, unfortunately. The only theory that comes to my mind is that t.trein_id.clone() + &t.stops[0].station.lange_naam could potentially generate non-unique keys. But that's just theory.
So it seems when I use the normal For component, there is no error of any kind. Would it be an idea to replace the set_value with try_set_value?
PRs are always welcome if that's a fix. But please provide a complete reproducible example as well. I'm not a fan of blindly changing the source code to suit individual use cases without even knowing them. It generates space for unintentional behavior and bugs. Also, without knowing the bugs being fixed, I cannot guarantee not reintroducing them with future updates :)
You're right.
So I found out what happened:
I used a signal string value in my view. This value is also used in an effect where I also modify the vector holding the items for animatedfor. It seems that using this string signal in my view causes the effect to run as well. Applying a derived signal to use as my label solved this issue.
So your first suspicion seems correct, the list is dissolved before AnimatedFor can properly run its code
I also run into this issue. What is the status or do we know what is causing it?
We know where it panics, but I couldn't create a reproducible app to better understand what's going on there and how it should be properly handled. So this issue currently awaits more interest from other people or me having a bit more spare time to debug it comprehensively.
If you could provide a code sample for this component where it breaks, that would greatly help me decide how to handle it. Maybe simply using try_set_value over set_value as already proposed would suffice. Maybe the use case isn't valid and the code should panic anyway but with a more descriptive message. Or maybe it is a more complex bug only manifesting itself as the panic.
I expect to have some spare time soon and will use that to rewrite this library for Leptos 0.7. If the bug proves difficult to fix, I may not solve it for Leptos 0.6, but at least I will test the rewritten version against it and make sure it works properly.
Can you mention where you think this try might work? Then i could try the patched version first before I try to make a example app.
I believe it was: https://github.com/brofrain/leptos-animated-for/blob/main/src/lib.rs#L78
It should be try_update_value not try_set_value 🙂
I was trying to debug it further a little and, even though I didn't manage to reproduce this, I think I know now what may be happening there. The component is probably updated and then removed before this future begins. The code there should preferably check if the component is still alive and return early in case it's not.
Let me know sir if try_update_value solves it for you. I'll release that little fix then and later get back to reproducing the case once I have the rewrite ready. I'll have to prepare a few E2E tests this time anyway 😄
@brofrain I tried to update them but it fails now deeper in the stack:
diff --git i/src/animator.rs w/src/animator.rs
index b3bbff8..10f288b 100644
--- i/src/animator.rs
+++ w/src/animator.rs
@@ -120,7 +120,7 @@ where
}
pub fn clear_transitions(&self) {
- self.transition_end_cbs.update_value(|v| {
+ self.transition_end_cbs.try_update_value(|v| {
for cb in v.drain(..) {
cb();
}
diff --git i/src/lib.rs w/src/lib.rs
index abb0f92..7a2a8df 100644
--- i/src/lib.rs
+++ w/src/lib.rs
@@ -75,7 +75,7 @@ where
let initial_children_mounted = StoredValue::new(false);
spawn_local(async move {
- initial_children_mounted.set_value(true);
+ initial_children_mounted.try_set_value(true);
});
(el_per_key, animator, move |item| {
The panic is now here:
src/lib.rs:196:17:
could not get stored value
It uses the with! macro but I don't know much of this one. Maybe you have a suggestion?
@johansmitsnl
try this branch: fix/5
I'm fixing it blindly, so there's no guarantee.
If that doesn't work, then I'll probably need a reproduction as I have very few ideas on how to trigger this scenario, and currently also not enough free time to really search for it.
If that does work, you should still take a closer look at your code if it runs in an optimal way. My assumption is that you are changing the items and then unmount the whole <AnimatedFor /> in the next browser tick. That would definitely be generating some unnecessary overhead.
Alternatively, it might be some other bug on my side that I'm not aware of.
@brofrain I tested the branch fix/5 doesn't crash anymore.
Published as of v0.4.8