mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* change suffixes from md to mdx fix broken links for English locale tree shake and update docusaurus add docusaurus ideal image plugin use svg and themed image delete unused static asset * move localized landing page * change GitLocalize project page * nit pick * remove ignore to have the block checked
414 lines
11 KiB
Plaintext
414 lines
11 KiB
Plaintext
---
|
|
title: "Pre-defined Hooks"
|
|
description: "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 `Deref`s 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`](#use_state_eq) if you want the component to only
|
|
re-render when the state changes.
|
|
|
|
### Example
|
|
|
|
```rust
|
|
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 an immutable reference to a value.
|
|
Its state persists across renders.
|
|
|
|
`use_ref` can be useful for keeping things in scope for the lifetime of the component, so long as
|
|
you don't store a clone of the resulting `Rc` anywhere that outlives the component.
|
|
|
|
If you need a mutable reference, consider using [`use_mut_ref`](#use_mut_ref).
|
|
If you need the component to be re-rendered on state change, consider using [`use_state`](#use_state).
|
|
|
|
```rust
|
|
// EventBus is an implementation of yew_agent::Agent
|
|
use website_test::agents::EventBus;
|
|
use yew::{function_component, html, use_ref, use_state, Callback};
|
|
use yew_agent::Bridged;
|
|
|
|
#[function_component(UseRef)]
|
|
fn ref_hook() -> Html {
|
|
let greeting = use_state(|| "No one has greeted me yet!".to_owned());
|
|
|
|
{
|
|
let greeting = greeting.clone();
|
|
use_ref(|| EventBus::bridge(Callback::from(move |msg| {
|
|
greeting.set(msg);
|
|
})));
|
|
}
|
|
|
|
html! {
|
|
<div>
|
|
<span>{ (*greeting).clone() }</span>
|
|
</div>
|
|
}
|
|
}
|
|
```
|
|
|
|
## `use_mut_ref`
|
|
`use_mut_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`](#use_state).
|
|
|
|
### Example
|
|
|
|
```rust
|
|
use web_sys::HtmlInputElement;
|
|
use yew::{
|
|
events::Event,
|
|
function_component, html, use_mut_ref, use_state,
|
|
Callback, TargetCast,
|
|
};
|
|
|
|
#[function_component(UseMutRef)]
|
|
fn mut_ref_hook() -> Html {
|
|
let message = use_state(|| "".to_string());
|
|
let message_count = use_mut_ref(|| 0);
|
|
|
|
let onclick = Callback::from(move |_| {
|
|
let window = gloo_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_node_ref`
|
|
`use_node_ref` is used for obtaining a `NodeRef` that persists across renders.
|
|
|
|
When conditionally rendering elements you can use `NodeRef` in conjunction with `use_effect_with_deps`
|
|
to perform actions each time an element is rendered and just before its going to be removed from the
|
|
DOM.
|
|
|
|
### Example
|
|
|
|
```rust
|
|
use web_sys::HtmlInputElement;
|
|
use yew::{
|
|
function_component, functional::*, html,
|
|
NodeRef
|
|
};
|
|
|
|
#[function_component(UseRef)]
|
|
pub fn ref_hook() -> Html {
|
|
let input_ref = use_node_ref();
|
|
let value = use_state(|| 25f64);
|
|
|
|
let onclick = {
|
|
let input_ref = input_ref.clone();
|
|
let value = value.clone();
|
|
move |_| {
|
|
if let Some(input) = input_ref.cast::<HtmlInputElement>() {
|
|
value.set(*value + input.value_as_number());
|
|
}
|
|
}
|
|
};
|
|
|
|
html! {
|
|
<div>
|
|
<input ref={input_ref} type="number" />
|
|
<button {onclick}>{ format!("Add input to {}", *value) }</button>
|
|
</div>
|
|
}
|
|
}
|
|
```
|
|
|
|
## `use_reducer`
|
|
|
|
`use_reducer` is an alternative to [`use_state`](#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`](#use_reducer_eq) if you want the component to only
|
|
re-render when the state changes.
|
|
|
|
### Example
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
gloo_utils::document().set_title(&format!("You clicked {} times", *counter));
|
|
|
|
// Perform the cleanup
|
|
|| gloo_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`](#use_effect). In such cases, we use `use_effect_with_deps`.
|
|
```rust ,no_run
|
|
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](../contexts.mdx) in function components.
|
|
|
|
|
|
### Example
|
|
|
|
```rust
|
|
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>
|
|
}
|
|
}
|
|
```
|