mirror of https://github.com/yewstack/yew
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 solves 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 a parent to a child.
|
|
They become cumbersome to pass down through deeply nested component trees 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 to reach the component that needs it.
|
|
This situation is called "Prop Drilling".
|
|
|
|
Consider the following example which passes down the theme using props:
|
|
|
|
```rust
|
|
use yew::{html, 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: Html,
|
|
}
|
|
|
|
#[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 a 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 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 the 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, context will likely help you.
|
|
Here are 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 currently 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, does not 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 that takes `articles` as a 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 the context value of a child
|
|
|
|
Because of Rust's ownership rules, a context cannot have a method that takes `&mut self` that can be called by children.
|
|
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)
|