189 lines
8.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 'コンテキスト (Contexts)'
sidebar_label: コンテキスト
description: 'コンテキストを使用して深くネストされたデータを渡す'
---
通常、データは props を介して親コンポーネントから子コンポーネントに渡されます。
しかし、多くの中間コンポーネントを介してデータを渡す必要がある場合や、アプリケーション内の多くのコンポーネントが同じ情報を必要とする場合、props を介してデータを渡すことは冗長で煩わしいものになります。
コンテキストはこの問題を解決し、親コンポーネントがデータをその下のツリー内の任意のコンポーネントに渡すことを可能にし、props を介してデータを渡す必要がなくなります。
## Props を使用する際の問題:"Prop Drilling"
[props](./function-components/properties.mdx) を介してデータを親コンポーネントから直接子コンポーネントに渡すことは良い方法です。
しかし、深くネストされたコンポーネントツリーを介してデータを渡す必要がある場合や、複数のコンポーネントが同じデータを共有する必要がある場合、props を渡すことは煩雑になります。
一般的なデータ共有の解決策は、データを共通の祖先に持ち上げ、子コンポーネントがそれを props として受け取るようにすることです。
しかし、これにより props が複数のコンポーネントを介して渡される必要がある場合があります。
この状況は "Prop Drilling" と呼ばれます。
以下の例を考えてみましょう。これは props を介してテーマを渡しています:
```rust
use yew::{html, Component, Context, Html, Properties, component};
#[derive(Clone, PartialEq)]
pub struct Theme {
foreground: String,
background: String,
}
#[derive(PartialEq, Properties)]
pub struct NavbarProps {
theme: Theme,
}
#[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,
}
#[component]
fn Title(_props: &ThemeProps) -> Html {
html! {
// impl
}
}
#[component]
fn NavButton(_props: &ThemeProps) -> Html {
html! {
// impl
}
}
/// アプリのルート
#[component]
fn App() -> Html {
let theme = Theme {
foreground: "yellow".to_owned(),
background: "pink".to_owned(),
};
html! {
<Navbar {theme} />
}
}
```
私たちはテーマ設定を `Navbar` に渡して、それが `Title` と `NavButton` に到達するようにしています。
もし `Title` と `NavButton` のようなテーマにアクセスする必要があるコンポーネントが、prop を介さずに直接テーマにアクセスできるとしたら、もっと良いでしょう。
コンテキストはこの問題を解決し、親コンポーネントがデータ(この場合はテーマ)をその子コンポーネントに渡すことを可能にします。
## コンテキストの使用
### ステップ 1コンテキストの提供
コンテキストを消費するには、コンテキストプロバイダーが必要です。`ContextProvider<T>` は、`T` がコンテキスト構造体として使用されるプロバイダーです。
`T` は `Clone` と `PartialEq` を実装する必要があります。`ContextProvider` は、その子コンポーネントがコンテキストを持つコンポーネントです。
コンテキストが変更されると、子コンポーネントは再レンダリングされます。データを渡すための構造体が定義されます。`ContextProvider` は次のように使用できます:
```rust
use yew::prelude::*;
/// アプリのテーマ
#[derive(Clone, Debug, PartialEq)]
struct Theme {
foreground: String,
background: String,
}
/// メインコンポーネント
#[component]
pub fn App() -> Html {
let ctx = use_state(|| Theme {
foreground: "#000000".to_owned(),
background: "#eeeeee".to_owned(),
});
html! {
// `ctx` は `Rc<UseStateHandle<Theme>>` 型であり、`Theme` が必要です
// したがって、デリファレンスします。
<ContextProvider<Theme> context={(*ctx).clone()}>
// ここにあるすべての子コンポーネントとその子コンポーネントは、このコンテキストにアクセスします。
<Toolbar />
</ContextProvider<Theme>>
}
}
/// ツールバー
/// このコンポーネントはコンテキストにアクセスできます。
#[component]
pub fn Toolbar() -> Html {
html! {
<div>
<ThemedButton />
</div>
}
}
/// `Toolbar` 内に配置されたボタン
/// このコンポーネントは、コンポーネントツリー内の `ThemeContextProvider` の子コンポーネントであるため、
/// コンテキストにアクセスできます。
#[component]
pub fn ThemedButton() -> Html {
let theme = use_context::<Theme>().expect("no ctx found");
html! {
<button style={format!("background: {}; color: {};", theme.background, theme.foreground)}>
{ "Click me!" }
</button>
}
}
```
### ステップ 2コンテキストの使用
#### 関数コンポーネント
`use_context` フックは、関数コンポーネント内でコンテキストを使用するために使用されます。
詳細については、[use_context ドキュメント](https://yew-rs-api.web.app/next/yew/functional/fn.use_context.html) を参照してください。
#### 構造体コンポーネント
構造体コンポーネント内でコンテキストを使用するには、2つの方法があります
- [高階コンポーネント](../advanced-topics/struct-components/hoc):高階関数コンポーネントがコンテキストを使用し、必要なデータを構造体コンポーネントに渡します。
- 構造体コンポーネント内で直接コンテキストを使用します。詳細については、[構造体コンポーネントのコンシューマーとしての例](https://github.com/yewstack/yew/tree/master/examples/contexts/src/struct_component_subscriber.rs) を参照してください。
## 使用シナリオ
通常、ツリーの異なる部分のリモートコンポーネントでデータを使用する必要がある場合、コンテキストが役立ちます。
以下はいくつかの例です:
- **テーマ**:アプリケーションのトップにコンテキストを配置し、アプリケーションのテーマを保持し、視覚的な外観を調整するために使用できます(上記の例を参照)。
- **現在のユーザーアカウント**:多くの場合、コンポーネントは現在ログインしているユーザーを知る必要があります。コンテキストを使用して、現在のユーザーオブジェクトをコンポーネントに提供できます。
### コンテキストを使用する前の考慮事項
コンテキストは非常に使いやすいですが、それが誤用/過度に使用される可能性もあります。
複数のレベル深いコンポーネントに props を共有するためにコンテキストを使用できるからといって、必ずしもそうすべきではありません。
例えば、コンポーネントを抽出して、そのコンポーネントを別のコンポーネントの子コンポーネントとして渡すことができます。
例えば、`Layout` コンポーネントが `articles` を prop として受け取り、それを `ArticleList` コンポーネントに渡す場合、
`Layout` コンポーネントをリファクタリングして、子コンポーネントを props として受け取り、`<Layout> <ArticleList {articles} /> </Layout>` と表示するようにするべきです。
## 子コンポーネントのコンテキスト値を変更する
Rust の所有権ルールにより、コンテキストには子コンポーネントが呼び出せる `&mut self` メソッドを持つことができません。
コンテキストの値を変更するには、リデューサーと組み合わせて使用する必要があります。これは、[`use_reducer`](https://yew-rs-api.web.app/next/yew/functional/fn.use_reducer.html) フックを使用して行うことができます。
[コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts) は、可変コンテキストの使用を示しています。
## さらなる読み物
- [コンテキストの例](https://github.com/yewstack/yew/tree/master/examples/contexts)