mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
* 0.20 Changelog * Improve changelog generator * Add blog post * Add blog post * Apply suggestions from code review Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com> Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com> * update Changelog * update Cargo.toml * changelog gen compiles * website version 0.20 * add migration guides * prettier * i18n Co-authored-by: WorldSEnder <WorldSEnder@users.noreply.github.com> Co-authored-by: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com>
175 lines
6.0 KiB
Plaintext
175 lines
6.0 KiB
Plaintext
---
|
|
title: 'Contexts'
|
|
sidebar_label: Contexts
|
|
description: 'Using contexts to pass deeply nested data'
|
|
---
|
|
|
|
Usually, data is passed from a parent component to a child component via props.
|
|
But passing props can become verbose and annoying if you have to pass them through many components in the middle,
|
|
or if many components in your app need the same information. Context solve this problem by allowing a
|
|
parent component to make data available to _any_ component in the tree below it, no matter how deep,
|
|
without having to pass it down with props.
|
|
|
|
## The problem with props: "Prop Drilling"
|
|
|
|
Passing [props](./function-components/properties.mdx) is a great way to pass data directly from parent to a child.
|
|
They become cumbersome to pass down through deeply nested component tree or when multiple components share the same data.
|
|
A common solution to data sharing is lifting the data to a common ancestor and making the children take it as props.
|
|
However, this can lead to cases where the prop has to go through multiple components in order to reach the component needs it.
|
|
This situation is called "Prop Drilling".
|
|
|
|
Consider the following example which passes down the theme using props:
|
|
|
|
```rust
|
|
use yew::{html, Children, Component, Context, Html, Properties, function_component};
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct Theme {
|
|
foreground: String,
|
|
background: String,
|
|
}
|
|
|
|
#[derive(PartialEq, Properties)]
|
|
pub struct NavbarProps {
|
|
theme: Theme,
|
|
}
|
|
|
|
#[function_component]
|
|
fn Navbar(props: &NavbarProps) -> Html {
|
|
html! {
|
|
<div>
|
|
<Title theme={props.theme.clone()}>
|
|
{ "App title" }
|
|
</Title>
|
|
<NavButton theme={props.theme.clone()}>
|
|
{ "Somewhere" }
|
|
</NavButton>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Properties)]
|
|
pub struct ThemeProps {
|
|
theme: Theme,
|
|
children: Children,
|
|
}
|
|
|
|
#[function_component]
|
|
fn Title(_props: &ThemeProps) -> Html {
|
|
html! {
|
|
// impl
|
|
}
|
|
}
|
|
|
|
#[function_component]
|
|
fn NavButton(_props: &ThemeProps) -> Html {
|
|
html! {
|
|
// impl
|
|
}
|
|
}
|
|
|
|
/// App root
|
|
#[function_component]
|
|
fn App() -> Html {
|
|
let theme = Theme {
|
|
foreground: "yellow".to_owned(),
|
|
background: "pink".to_owned(),
|
|
};
|
|
|
|
html! {
|
|
<Navbar {theme} />
|
|
}
|
|
}
|
|
```
|
|
|
|
We "drill" the theme prop through `Navbar` so that it can reach `Title` and `NavButton`.
|
|
It would be nice if `Title` and `NavButton`, the components that need access to the theme, can just access the theme
|
|
without having to pass it to them as prop. Contexts solve this problem by allowing a parent to pass data, theme in this case,
|
|
to its children.
|
|
|
|
## Using Contexts
|
|
|
|
### Step 1: Providing the context
|
|
|
|
A context provider is required to consume the context. `ContextProvider<T>`, where `T` is the context struct is used as the provider.
|
|
`T` must implement `Clone` and `PartialEq`. `ContextProvider` is the component whose children will have the context available to them.
|
|
The children are re-rendered when the context changes. A struct is used to define what data is to be passed. The `ContextProvider` can be used as:
|
|
|
|
```rust
|
|
use std::rc::Rc;
|
|
use yew::prelude::*;
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
struct Theme {
|
|
foreground: String,
|
|
background: String,
|
|
}
|
|
|
|
#[function_component]
|
|
fn NavButton() -> Html {
|
|
let theme = use_context::<Theme>();
|
|
|
|
html! {
|
|
// use theme
|
|
}
|
|
}
|
|
|
|
#[function_component]
|
|
fn App() -> Html {
|
|
let theme = use_memo(|_| Theme {
|
|
foreground: "yellow".to_owned(),
|
|
background: "pink".to_owned(),
|
|
}, ());
|
|
|
|
html! {
|
|
<ContextProvider<Rc<Theme>> context={theme}>
|
|
<NavButton />
|
|
</ContextProvider<Rc<Theme>>>
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 2: Consuming context
|
|
|
|
#### Function components
|
|
|
|
`use_context` hook is used to consume contexts in function components.
|
|
See [docs for use_context](https://yew-rs-api.web.app/next/yew/functional/fn.use_context.html) to learn more.
|
|
|
|
#### Struct components
|
|
|
|
We have 2 options to consume contexts in struct components:
|
|
|
|
- [Higher Order Components](../advanced-topics/struct-components/hoc.mdx): A higher order function component will consume the context and pass the data to the struct component which requires it.
|
|
- Consume context directly in struct component. See [example of struct component as a consumer](https://github.com/yewstack/yew/tree/master/examples/contexts/src/struct_component_subscriber.rs)
|
|
|
|
## Use cases
|
|
|
|
Generally, if some data is needed by distant components in different parts of the tree, it's likely that context will help you.
|
|
Here's some examples of such cases:
|
|
|
|
- **Theming**: You can put a context at the top of the app that holds your app theme and use it to adjust the visual appearance, as shown in the above example.
|
|
- **Current user account**: In many cases, components need to know the current logged-in user. You can use a context to provide the current user object to the components.
|
|
|
|
### Considerations to make before using contexts
|
|
|
|
Contexts are very easy to use. That makes them very easy to misuse/overuse.
|
|
Just because you can use a context to share props to components multiple levels deep, doesn't mean that you should.
|
|
|
|
For example, you may be able to extract a component and pass that component as a child to another component. For example,
|
|
you may have a `Layout` component which takes `articles` as prop and passes it down to `ArticleList` component.
|
|
You should refactor the `Layout` component to take children as props and display `<Layout> <ArticleList {articles} /> </Layout>`.
|
|
|
|
## Mutating context value a child
|
|
|
|
Because of Rust's ownership rules, a context cannot have a method that takes `&mut self` that can be called by children.
|
|
In order to mutate a context's value, we must combine it with a reducer. This is done by using the
|
|
[`use_reducer`](https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html) hook.
|
|
|
|
The [contexts example](https://github.com/yewstack/yew/tree/master/examples/contexts) demonstrates mutable contexts
|
|
with the help of contexts
|
|
|
|
## Further reading
|
|
|
|
- The [contexts example](https://github.com/yewstack/yew/tree/master/examples/contexts)
|