mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
189 lines
8.9 KiB
Plaintext
189 lines
8.9 KiB
Plaintext
---
|
||
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)
|