State Migration
The use_state and use_ref hooks have been replaced with the use_signal hook. The use_signal hook is a more flexible and powerful version of the use_ref hook with smarter scopes that only subscribe to a signal if that signal is read within the scope.
With use_state, if you had this code:
fn Parent(cx: Scope) -> Element {
let state = use_state(cx, || 0);
render! {
Child {
state: state.clone()
}
}
}
#[component]
fn Child(cx: Scope, state: UseState<i32>) -> Element {
render! {
"{state}"
}
}Parent would re-render every time the state changed even though only the child component was using the state. With the new use_signal hook, the parent would only re-render if the state was changed within the parent component:
fn Parent() -> Element {
let state = use_signal(|| 0);
rsx! { Child { state } }
}
#[component]
fn Child(state: Signal<i32>) -> Element {
rsx! {"{state}"}
}Only the child component will re-render when the state changes because only the child component is reading the state.
Context Based State
The use_shared_state_provider and use_shared_state hooks have been replaced with using the use_context_provider and use_context hooks with a Signal:
fn Parent() -> Element {
// Create a new signal and provide it to the context API
let state = use_context_provider(|| Signal::new(0));
rsx! { Child {} }
}
fn Child() -> Element {
// Get the state from the context API
let state = use_context::<Signal<i32>>();
rsx! {"{state}"}
}Signals are smart enough to handle subscribing to the right scopes without a special shared state hook.
Opting Out of Subscriptions
Some state hooks including use_shared_state and use_ref hooks had a function called write_silent in 0.4. This function allowed you to update the state without triggering a re-render any subscribers. This function has been removed in 0.5.
Instead, you can use the peek function to read the current value of a signal without subscribing to it. This inverts the subscription model so that you can opt out of subscribing to a signal instead of opting all subscribers out of updates:
fn Parent() -> Element {
let state = use_signal(|| 0);
// Even though we are reading the state, we don't need to subscribe to it
let read_without_subscribing = state.peek();
println!("{}", state.peek());
rsx! { Child { state } }
}
#[component]
fn Child(state: Signal<i32>) -> Element {
rsx! {
button { onclick: move |_| {
state += 1;
}, "count is {state}" }
}
}peek gives you more fine-grained control over when you want to subscribe to a signal. This can be useful for performance optimizations and for updating state without re-rendering components.
Global State
In 0.4, the fermi crate provided a separate global state API called atoms. In 0.5, the Signal type has been extended to provide a global state API. You can use the Signal::global function to create a global signal:
static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
fn Parent() -> Element {
rsx! {
div { "{COUNT}" }
button {
onclick: move |_| {
*COUNT.write() += 1;
},
"Increment"
}
}
}You can read more about global signals in the Fermi migration guide.