Why does use_state
not update its value in a simple interval counter?
#3091
-
Here is the example with a simple use gloo::timers::callback::Interval;
use yew::prelude::*;
#[function_component(Timer)]
fn component() -> Html {
let active = use_state(|| true);
let counter = use_state(|| 0);
let start_timer = {
let active = active.clone();
let counter = counter.clone();
Callback::from(move |_| {
active.set(false);
let counter = counter.clone();
Interval::new(1_000, move || {
counter.set(*counter + 1);
}).forget();
})
};
html! {
<button disabled={!*active} onclick={start_timer}>
{*counter}
</button>
}
}
#[function_component(App)]
fn app() -> Html {
html! {
<Timer />
}
}
fn main() {
yew::Renderer::<App>::new().render();
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
The As such, use std::rc::Rc;
#[derive(Clone)]
struct Counter { value: u32 }
impl Reducible for Counter {
type Action = (); // Only one action. Could use an enum CounterAction { Increment } here for more clarity
fn reduce(mut self: Rc<Self>, _action: ()) -> Rc<Self> {
let this = Rc::make_mut(&mut self);
this.value += 1;
self
}
}
#[function_component]
fn Timer() -> Html {
let active = use_state(|| true);
let counter = use_reducer(|| Counter { value: 0 });
let start_timer = {
let active = active.clone();
let counter_dispatch = counter.dispatcher();
Callback::from(move |_| {
active.set(false);
let counter_dispatch = counter_dispatch.clone();
Interval::new(1_000, move || {
counter_dispatch.dispatch(()); // Dispatch the action
}).forget();
})
};
html! {
<button disabled={!*active} onclick={start_timer}>
{counter.value}
</button>
}
} |
Beta Was this translation helpful? Give feedback.
The
UseStateHandle
you get during rendering captures and indefinitely reflects the value the state had at the time of rendering. This is so that using the state handle as a cache key works correctly, and compares the old vs the new value, when using the "same" handle from successive renders.As such,
*counter + 1
always computes the same value: 1 + the counter value at the time of rendering. What you want to use isReducible
.Reducible::reduce
does get access to the current value, even when an action is dispatched later: