mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
parent
b433074ef5
commit
4bc61b8da9
@ -1,12 +1,25 @@
|
||||
---
|
||||
title: "Contexts"
|
||||
sidebar_label: Contexts
|
||||
description: "Using contexts to pass data within application"
|
||||
description: "Using contexts to pass deeply nested data"
|
||||
---
|
||||
|
||||
Generally data is passed down the component tree using props but that becomes tedious for values such as
|
||||
user preferences, authentication information etc. Consider the following example which passes down the
|
||||
theme using props:
|
||||
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) 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};
|
||||
@ -48,6 +61,7 @@ fn Title(_props: &ThemeProps) -> Html {
|
||||
// impl
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
fn NavButton(_props: &ThemeProps) -> Html {
|
||||
html! {
|
||||
@ -55,41 +69,68 @@ fn NavButton(_props: &ThemeProps) -> Html {
|
||||
}
|
||||
}
|
||||
|
||||
// root
|
||||
let theme = Theme {
|
||||
foreground: "yellow".to_owned(),
|
||||
background: "pink".to_owned(),
|
||||
};
|
||||
/// App root
|
||||
#[function_component]
|
||||
fn App() -> Html {
|
||||
let theme = Theme {
|
||||
foreground: "yellow".to_owned(),
|
||||
background: "pink".to_owned(),
|
||||
};
|
||||
|
||||
html! {
|
||||
<Navbar {theme} />
|
||||
};
|
||||
html! {
|
||||
<Navbar {theme} />
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Passing down data like this isn't ideal for something like a theme which needs to be available everywhere.
|
||||
This is where contexts come in.
|
||||
|
||||
Contexts can be understood as a global-ish opt in props.
|
||||
You need to provide the value somewhere in the component tree and all sub-components will be able to listen into its value and changes.
|
||||
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
|
||||
|
||||
In order to use contexts, we need a struct which defines what data is to be passed.
|
||||
For the above use-case, consider the following struct:
|
||||
### 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>>>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Consuming context
|
||||
### Step 2: Consuming context
|
||||
|
||||
#### Function components
|
||||
|
||||
@ -100,6 +141,35 @@ See [docs for use_context](function-components/hooks/use-context) to learn more.
|
||||
|
||||
We have 2 options to consume contexts in struct components:
|
||||
|
||||
- [Higher Order Components](../advanced-topics/struct-components/hoc)
|
||||
A higher order function component will consume the context and pass the data to the struct component which requires it.
|
||||
- [Higher Order Components](../advanced-topics/struct-components/hoc): 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`](./function_component/hooks/use_reducer) 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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user