* Add Redirect Comp. * Fix router behaviour. * Fix output. * Fix pr-flow. * Remove Redirect. * Readd 77b46bf. * Reliable dispatch for use_reducer. * Detachable Dispatcher and Setter. * Update docs wording. * Update website/docs/concepts/function-components/pre-defined-hooks.md Co-authored-by: Simon <simon@siku2.io> * Update website/docs/concepts/function-components/pre-defined-hooks.md Co-authored-by: Simon <simon@siku2.io> * Update website/docs/concepts/function-components/pre-defined-hooks.md Co-authored-by: Simon <simon@siku2.io> Co-authored-by: Simon <simon@siku2.io>
9.4 KiB
| title | description |
|---|---|
| Pre-defined Hooks | The pre-defined Hooks that Yew comes with |
use_state
use_state is used to manage state in a function component.
It returns a UseStateHandle object which Derefs to the current value
and provides a set method to update the value.
The hook takes a function as input which determines the initial state. This value remains up-to-date on subsequent renders.
The setter function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the UseStateHandle from the
dependents of use_effect_with_deps if you only intend to set
values from within the hook.
This hook will always trigger a re-render upon receiving a new state. See
use_state_eq if you want the component to only
re-render when the state changes.
Example
use yew::{Callback, function_component, html, use_state};
#[function_component(UseState)]
fn state() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<div>
<button {onclick}>{ "Increment value" }</button>
<p>
<b>{ "Current value: " }</b>
{ *counter }
</p>
</div>
}
}
:::caution
The value held in the handle will reflect the value at the time the
handle is returned by the use_state. It is possible that the handle
does not dereference to an up to date value if you are moving it into a
use_effect_with_deps hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
use_state_eq
This hook has the same effect as use_state but will only trigger a
re-render when the setter receives a value that prev_state != next_state.
This hook requires the state object to implement PartialEq.
use_ref
use_ref is used for obtaining a mutable reference to a value.
Its state persists across renders.
It is important to note that you do not get notified of state changes.
If you need the component to be re-rendered on state change, consider using use_state.
Example
use web_sys::HtmlInputElement;
use yew::{
events::Event,
function_component, html, use_ref, use_state,
Callback, TargetCast,
};
#[function_component(UseRef)]
fn ref_hook() -> Html {
let message = use_state(|| "".to_string());
let message_count = use_ref(|| 0);
let onclick = Callback::from(move |_| {
let window = yew::utils::window();
if *message_count.borrow_mut() > 3 {
window.alert_with_message("Message limit reached").unwrap();
} else {
*message_count.borrow_mut() += 1;
window.alert_with_message("Message sent").unwrap();
}
});
let onchange = {
let message = message.clone();
Callback::from(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
message.set(input.value());
})
};
html! {
<div>
<input {onchange} value={(*message).clone()} />
<button {onclick}>{ "Send" }</button>
</div>
}
}
use_reducer
use_reducer is an alternative to use_state. It is used to handle component's state and is used
when complex actions needs to be performed on said state.
It accepts an initial state function and returns a UseReducerHandle that dereferences to the state,
and a dispatch function.
The dispatch function takes one argument of type Action. When called, the action and current value
are passed to the reducer function which computes a new state which is returned,
and the component is re-rendered.
The dispatch function is guaranteed to be the same across the entire
component lifecycle. You can safely omit the UseReducerHandle from the
dependents of use_effect_with_deps if you only intend to dispatch
values from within the hooks.
The state object returned by the initial state function is required to
implement a Reducible trait which provides an Action type and a
reducer function.
This hook will always trigger a re-render upon receiving an action. See
use_reducer_eq if you want the component to only
re-render when the state changes.
Example
use yew::prelude::*;
use std::rc::Rc;
/// reducer's Action
enum CounterAction {
Double,
Square,
}
/// reducer's State
struct CounterState {
counter: i32,
}
impl Default for CounterState {
fn default() -> Self {
Self { counter: 1 }
}
}
impl Reducible for CounterState {
/// Reducer Action Type
type Action = CounterAction;
/// Reducer Function
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
let next_ctr = match action {
CounterAction::Double => self.counter * 2,
CounterAction::Square => self.counter.pow(2)
};
Self { counter: next_ctr }.into()
}
}
#[function_component(UseReducer)]
fn reducer() -> Html {
// The use_reducer hook takes an initialization function which will be called only once.
let counter = use_reducer(CounterState::default);
let double_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Double))
};
let square_onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.dispatch(CounterAction::Square))
};
html! {
<>
<div id="result">{ counter.counter }</div>
<button onclick={double_onclick}>{ "Double" }</button>
<button onclick={square_onclick}>{ "Square" }</button>
</>
}
}
:::caution
The value held in the handle will reflect the value of at the time the
handle is returned by the use_reducer. It is possible that the handle does
not dereference to an up to date value if you are moving it into a
use_effect_with_deps hook. You can register the
state to the dependents so the hook can be updated when the value changes.
:::
use_reducer_eq
This hook has the same effect as use_reducer but will only trigger a
re-render when the reducer function produces a value that prev_state != next_state.
This hook requires the state object to implement PartialEq in addition
to the Reducible trait required by use_reducer.
use_effect
use_effect is used for hooking into the component's lifecycle.
Similar to rendered from the Component trait,
use_effect takes a function which is called after the render finishes.
The input function has to return a closure, the destructor, which is called when the component is destroyed. The destructor can be used to clean up the effects introduced and it can take ownership of values to delay dropping them until the component is destroyed.
Example
use yew::{Callback, function_component, html, use_effect, use_state};
#[function_component(UseEffect)]
fn effect() -> Html {
let counter = use_state(|| 0);
{
let counter = counter.clone();
use_effect(move || {
// Make a call to DOM API after component is rendered
yew::utils::document().set_title(&format!("You clicked {} times", *counter));
// Perform the cleanup
|| yew::utils::document().set_title("You clicked 0 times")
});
}
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
html! {
<button {onclick}>{ format!("Increment to {}", *counter) }</button>
}
}
use_effect_with_deps
Sometimes, it's needed to manually define dependencies for use_effect. In such cases, we use use_effect_with_deps.
use yew::use_effect_with_deps;
use_effect_with_deps(
move |_| {
// ...
|| ()
},
(), // dependents
);
Note: dependents must implement PartialEq.
use_context
use_context is used for consuming contexts in function components.
Example
use yew::{ContextProvider, function_component, html, use_context, use_state};
/// App theme
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
/// Main component
#[function_component(App)]
pub fn app() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
// `ctx` is type `Rc<UseStateHandle<Theme>>` while we need `Theme`
// so we deref it.
// It derefs to `&Theme`, hence the clone
<ContextProvider<Theme> context={(*ctx).clone()}>
// Every child here and their children will have access to this context.
<Toolbar />
</ContextProvider<Theme>>
}
}
/// The toolbar.
/// This component has access to the context
#[function_component(Toolbar)]
pub fn toolbar() -> Html {
html! {
<div>
<ThemedButton />
</div>
}
}
/// Button placed in `Toolbar`.
/// As this component is a child of `ThemeContextProvider` in the component tree, it also has access to the context.
#[function_component(ThemedButton)]
pub fn themed_button() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}
}