yew/website/docs/concepts/contexts.md
mc1098 ed2e1ea00e
Add testing for website code blocks (#2014)
* Add doc-test to test website code snippets

Heavily inspired by tokio-rs/website repo.

* Fix code snippets to pass doc tests

Some code snippets are explicitly ignored and some are not run
to avoid having to include dependencies for one liners.

* Add website code snippet tests to CI

* Fix CI

* Remove doc-test from workspace

* Exclude doc-test from workspace

* Refactor code snippets and tests

Code snippets can import types from doc_test crate i.e.:
```rust
use doc_test::agents::EventBus;
```
This allows for moving some boilerplate away from the example and still
checks that the code compiles correctly.

Also some slight changes to some of the examples and the information
about `ComponentLink` which is deprecated.

* Move doc-test to packages

* Rename doc-test crate to website-test

The new name makes it more clear the purpose of this crate.

* fix ci
2021-08-28 13:17:28 +02:00

3.6 KiB

title sidebar_label description
Contexts Contexts Using contexts to pass data within application

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:

use yew::{html, Children, Component, Context, Html, Properties};

#[derive(Clone, PartialEq)]
pub struct Theme {
    foreground: String,
    background: String,
}

#[derive(PartialEq, Properties)]
pub struct NavbarProps {
    theme: Theme,
}

pub struct Navbar;

impl Component for Navbar {
    type Message = ();
    type Properties = NavbarProps;

    fn create(_ctx: &Context<Self>) -> Self {
        Self
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        html! {
            <div>
                <Title theme={ctx.props().theme.clone()}>
                    { "App title" }
                </Title>
                <NavButton theme={ctx.props().theme.clone()}>
                    { "Somewhere" }
                </NavButton>
            </div>
        }
    }
}

#[derive(PartialEq, Properties)]
pub struct ThemeProps {
    theme: Theme,
    children: Children,
}

#[yew::function_component(Title)]
fn title(_props: &ThemeProps) -> Html {
    html! {
        // impl
    }
}
#[yew::function_component(NavButton)]
fn nav_button(_props: &ThemeProps) -> Html {
    html! {
        // impl
    }
}

// root
let theme = Theme {
    foreground: "yellow".to_owned(),
    background: "pink".to_owned(),
};

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 provide a way to share data between components without passing them down explicitly as props. They make data available to all components in the tree.

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:

#[derive(Clone, Debug, PartialEq)]
struct Theme {
    foreground: String,
    background: String,
}

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

Struct components

The Scope::context method is used to consume contexts in struct components.

Example
use yew::{Callback, html, Component, Context, Html};

#[derive(Clone, Debug, PartialEq)]
struct Theme {
    foreground: String,
    background: String,
}

struct ContextDemo;

impl Component for ContextDemo {
    type Message = ();
    type Properties = ();

    fn create(_ctx: &Context<Self>) -> Self {
        Self
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let (theme, _) = ctx
            .link()
            .context::<Theme>(Callback::noop())
            .expect("context to be set");
        html! {
            <button style={format!(
                    "background: {}; color: {};", 
                    theme.background, 
                    theme.foreground
                )}
            >
                { "Click me!" }
            </button>
        }
    }
}

Function components

use_context hook is used to consume contexts in function components. See docs for use_context to learn more.