provider-pattern
コンポーネントツリー全体でデータを共有するためのプロバイダーパターンを解説します。深くネストされた複数のコンポーネントが同じデータにアクセスする必要があり、propsのバケツリレーが煩雑になってきた際に活用してください。
description の原文を見る
Teaches the provider pattern for sharing data across component trees. Use when multiple nested components need access to the same data and prop drilling becomes unwieldy.
SKILL.md 本文
Provider パターン
目次
場合によっては、アプリケーション内のほぼすべてのコンポーネント(またはすべてのコンポーネント)でデータを利用可能にしたいことがあります。propsを使ってコンポーネントにデータを渡すことはできますが、アプリケーション内のほぼすべてのコンポーネントが props の値にアクセスする必要がある場合、これは困難になる可能性があります。
しばしば prop drilling と呼ばれる問題が発生します。これはコンポーネントツリーの深い層まで props を渡す場合です。props に依存するコードのリファクタリングはほぼ不可能になり、特定のデータがどこから来ているのかを知ることが困難になります。
使用時機
- 多くのコンポーネントが同じデータ(テーマ、認証、ロケール)にアクセスする必要がある場合に使用してください
- 複数のコンポーネント層にまたがって prop drilling が厄介になる場合に役立ちます
手順
React.createContext()で Context を作成し、その Provider でコンポーネントをラップする- 消費側のコンポーネントで
useContextフックを使用して提供されている値にアクセスする - カスタムフック(例:
useThemeContext)を作成して context の消費ロジックをカプセル化する - 頻繁に更新される値に対して context の過剰使用を避ける。すべての消費者は変更時に再レンダリングされるため
- 関心事ごとに context を分割して、不要な再レンダリングを最小化する
詳細
App コンポーネントが特定のデータを含んでいるとします。コンポーネントツリーの深い層に、このデータすべてにアクセスする必要がある ListItem、Header、Text コンポーネントがあります。このデータをこれらのコンポーネントに渡すには、複数の層のコンポーネントを通してそれを渡す必要があります。
コードベースでは、以下のようになります:
function App() {
const data = { ... }
return (
<div>
<SideBar data={data} />
<Content data={data} />
</div>
)
}
const SideBar = ({ data }) => <List data={data} />
const List = ({ data }) => <ListItem data={data} />
const ListItem = ({ data }) => <span>{data.listItem}</span>
const Content = ({ data }) => (
<div>
<Header data={data} />
<Block data={data} />
</div>
)
const Header = ({ data }) => <div>{data.title}</div>
const Block = ({ data }) => <Text data={data} />
const Text = ({ data }) => <h1>{data.text}</h1>
このようにして props を渡すのは非常に面倒になります。将来 data prop の名前を変更したい場合、すべてのコンポーネントで名前を変更する必要があります。アプリケーションが大きくなるほど、prop drilling はより複雑になります。
このデータを必要としないすべての層のコンポーネントをスキップできるのが理想的です。data の値にアクセスする必要があるコンポーネントに、prop drilling に頼ることなく直接アクセスを与えるものが必要です。
ここで Provider パターン が役に立ちます!Provider パターンを使用すると、複数のコンポーネントでデータを利用可能にできます。そのデータを各層で props を通して下に渡す代わりに、すべてのコンポーネントを Provider でラップできます。Provider は Context オブジェクトによって提供される高階コンポーネントです。React が提供する createContext メソッドを使用して Context オブジェクトを作成できます。
Provider は value prop を受け取ります。これには下に渡したいデータが含まれます。この provider 内にラップされたすべてのコンポーネントは value prop の値にアクセスできます。
const DataContext = React.createContext()
function App() {
const data = { ... }
return (
<div>
<DataContext.Provider value={data}>
<SideBar />
<Content />
</DataContext.Provider>
</div>
)
}
各コンポーネントに data prop を手動で渡す必要がもうありません!各コンポーネントは useContext フックを使用して data にアクセスできます。このフックは data が参照を持つ context を受け取ります。この場合は DataContext です。useContext フックを使用すると、context オブジェクトのデータを読み書きできます。
const DataContext = React.createContext();
function App() {
const data = { ... }
return (
<div>
<DataContext.Provider value={data}>
<SideBar />
<Content />
</DataContext.Provider>
</div>
)
}
const SideBar = () => <List />
const List = () => <ListItem />
const Content = () => <div><Header /><Block /></div>
function ListItem() {
const { data } = React.useContext(DataContext);
return <span>{data.listItem}</span>;
}
function Text() {
const { data } = React.useContext(DataContext);
return <h1>{data.text}</h1>;
}
function Header() {
const { data } = React.useContext(DataContext);
return <div>{data.title}</div>;
}
data 値を使用していないコンポーネントは data と一切やり取りする必要がありません。複数の層を通してこのコンポーネントが必要としていない props を下に渡すことについて心配する必要がなくなり、リファクタリングがはるかに簡単になります。
Provider パターンはグローバルデータを共有するのに非常に便利です。Provider パターンの一般的なユースケースは、テーマ UI ステートを多くのコンポーネントと共有することです。
リストを表示する単純なアプリがあるとしましょう。ユーザーがスイッチを切り替えることで、ライトモードとダークモードを切り替えられるようにしたいです。ユーザーがダークモードからライトモードに、またはその逆に切り替わる場合、背景色とテキスト色が変更されるはずです!現在のテーマ値を各コンポーネントに下に渡す代わりに、コンポーネントを ThemeProvider でラップし、現在のテーマカラーを provider に渡すことができます。
export const ThemeContext = React.createContext();
const themes = {
light: {
background: "#fff",
color: "#000",
},
dark: {
background: "#171717",
color: "#fff",
},
};
export default function App() {
const [theme, setTheme] = useState("dark");
function toggleTheme() {
setTheme(theme === "light" ? "dark" : "light");
}
const providerValue = {
theme: themes[theme],
toggleTheme,
};
return (
<div className={`App theme-${theme}`}>
<ThemeContext.Provider value={providerValue}>
<Toggle />
<List />
</ThemeContext.Provider>
</div>
);
}
Toggle と List コンポーネントの両方が ThemeContext provider 内にラップされているため、provider に value として渡される theme と toggleTheme の値にアクセスできます。
Toggle コンポーネント内で、toggleTheme 関数を使用してテーマを適切に更新できます。
import React, { useContext } from "react";
import { ThemeContext } from "./App";
export default function Toggle() {
const theme = useContext(ThemeContext);
return (
<label className="switch">
<input type="checkbox" onClick={theme.toggleTheme} />
<span className="slider round" />
</label>
);
}
List コンポーネント自体は現在のテーマの値を気にしません。しかし、ListItem コンポーネントは気にします!テーマ context を ListItem 内で直接使用できます。
import React, { useContext } from "react";
import { ThemeContext } from "./App";
export default function TextBox() {
const theme = useContext(ThemeContext);
return <li style={theme.theme}>...</li>;
}
完璧です!現在のテーマの値を気にしないコンポーネントにデータを下に渡す必要がありませんでした。
フック
コンポーネントに context を提供するフックを作成できます。各コンポーネントで useContext と Context をインポートする代わりに、必要な context を返すフックを使用できます。
function useThemeContext() {
const theme = useContext(ThemeContext);
return theme;
}
それが有効なテーマであることを確認するために、useContext(ThemeContext) が falsy の値を返す場合はエラーをスローしましょう。
function useThemeContext() {
const theme = useContext(ThemeContext);
if (!theme) {
throw new Error("useThemeContext must be used within ThemeProvider");
}
return theme;
}
ThemeContext.Provider コンポーネントでコンポーネントを直接ラップする代わりに、専用の provider コンポーネントを抽出できます。これは context ロジックをレンダリングコンポーネントから分離し、再利用性を向上させます。
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("dark");
function toggleTheme() {
setTheme(theme === "light" ? "dark" : "light");
}
const providerValue = {
theme: themes[theme],
toggleTheme,
};
return (
<ThemeContext.Provider value={providerValue}>
{children}
</ThemeContext.Provider>
);
}
export default function App() {
return (
<ThemeProvider>
<div className="App">
<Toggle />
<List />
</div>
</ThemeProvider>
);
}
ThemeContext にアクセスする必要があるコンポーネントは、useThemeContext フックを単純に使用できます。
export default function TextBox() {
const theme = useThemeContext();
return <li style={theme.theme}>...</li>;
}
異なる context 用のフックを作成することで、provider のロジックをデータをレンダリングするコンポーネントから簡単に分離できます。
ケーススタディ
一部のライブラリは組み込み provider を提供しており、これらの値は消費側のコンポーネントで使用できます。この良い例は styled-components です。
styled-components ライブラリは ThemeProvider を提供してくれます。各 styled component はこの provider の値にアクセスできます!Context API を自分たちで作成する代わりに、提供されているものを使用できます!
import { ThemeProvider } from "styled-components";
export default function App() {
const [theme, setTheme] = useState("dark");
function toggleTheme() {
setTheme(theme === "light" ? "dark" : "light");
}
return (
<div className={`App theme-${theme}`}>
<ThemeProvider theme={themes[theme]}>
<Toggle toggleTheme={toggleTheme} />
<List />
</ThemeProvider>
</div>
);
}
ListItem コンポーネントにインライン style prop を渡す代わりに、styled.li コンポーネントにします。それは styled component なので、theme の値にアクセスできます!
import styled from "styled-components";
export default function ListItem() {
return (
<Li>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</Li>
);
}
const Li = styled.li`
${({ theme }) => `
background-color: ${theme.backgroundColor};
color: ${theme.color};
`}
`;
これで ThemeProvider を使用してすべての styled component に簡単にスタイルを適用できます!
トレードオフ
長所
Provider パターン/Context API は、各コンポーネント層を手動で通してデータを渡すことなく、多くのコンポーネントにデータを渡すことを可能にします。
コードをリファクタリングする際に意図せずバグを導入するリスクを軽減します。以前は、後で prop の名前を変更したい場合、この値が使用されたアプリケーション全体でこの prop の名前を変更する必要がありました。
もはや prop drilling に対処する必要がなくなりました。これはアンチパターンと見なされる可能性があります。以前は、特定の prop 値がどこから来たのかが常に明らかではないため、アプリケーションのデータフローを理解することが困難でした。Provider パターンでは、このデータを気にしないコンポーネントに不要に props を渡す必要がなくなりました。
何らかのグローバル状態を保持することは Provider パターンで簡単になります。コンポーネントにこのグローバル状態へのアクセスを与えることができるためです。
短所
場合によっては、Provider パターンの過剰使用はパフォーマンスの問題につながる可能性があります。context を 消費する すべてのコンポーネントは各状態変更で再レンダリングされます。
例を見てみましょう。Button コンポーネントの Increment ボタンをクリックするたびに値が増加する単純なカウンターがあります。また、カウントを 0 にリセットする Reset ボタンが Reset コンポーネントにあります。
Reset コンポーネントは useCountContext を消費していたため、再レンダリングされました。より小さいアプリケーションではこれはそれほど重要ではありません。より大きいアプリケーションでは、頻繁に更新される値を多くのコンポーネントに渡すと、パフォーマンスに悪影響を与える可能性があります。
コンポーネントが更新される可能性のある不要な値を含む provider を消費していないことを確認するために、各ユースケースに対して複数の provider を作成できます。
ソース
参考資料
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- patternsdev
- リポジトリ
- patternsdev/skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/patternsdev/skills / ライセンス: MIT
関連スキル
hugging-face-trackio
Trackioを使用してMLトレーニング実験を追跡・可視化できます。トレーニング中のメトリクスログ記録(Python API)、トレーニング診断のアラート発火、ログされたメトリクスの取得・分析(CLI)が必要な場合に活用してください。リアルタイムダッシュボード表示、Webhookを使用したアラート、HF Space同期、自動化向けのJSON出力に対応しています。
btc-bottom-model
ビットコインのサイクルタイミングモデルで、加重スコアリングシステムを搭載しています。日次パルス(4指標、32ポイント)とウィークリー構造(9指標、68ポイント)の2カテゴリーにわたる13の指標を追跡し、0~100のマーケットヒートスコアを算出します。ETFフロー、ファンディングレート、ロング/ショート比率、恐怖・貪欲指数、LTH-MVRV、NUPL、SOPR(LTH+STH)、LTH供給率、移動平均倍率(365日MA、200週MA)、週次RSI、出来高トレンドに対応します。市場サイクル全体を通じて買いと売りの両方の推奨を提供します。ビットコインの底値拾い、BTCサイクルポジション、買い時・売り時、オンチェーン指標、MVRV、NUPL、SOPR、LTH動向、ETFの流出入、ファンディングレート、恐怖指数、ビットコインが過熱状態か、マイナーコスト、暗号資産市場のセンチメント、BTCのポジションサイジング、「今ビットコインを買うべきか」「BTCが天井をつけているか」「オンチェーン指標は何を示しているか」といった質問の際にこのスキルを活用します。
protein_solubility_optimization
タンパク質の溶解性最適化 - タンパク質の溶解性を最適化します。タンパク質の特性を計算し、溶解性と親水性を予測し、有効な変異を提案します。タンパク質配列の特性計算、タンパク質機能の予測、親水性計算、ゼロショット配列予測を含むタンパク質エンジニアリング業務に使用できます。3つのSCPサーバーから4つのツールを統合しています。
research-lookup
Parallel Chat APIまたはPerplexity sonar-pro-searchを使用して、最新の研究情報を検索できます。学術論文の検索にも対応しています。クエリは自動的に最適なバックエンドにルーティングされるため、論文の検索、研究データの収集、科学情報の検証に活用できます。
tree-formatting
ggtree(R)またはiTOL(ウェブ)を使用して、系統樹の可視化とフォーマットを行います。系統樹を図として描画する際、ツリーレイアウトの選択、分類学に基づく枝やラベルの色付け、クレードの折りたたみ、サポート値の表示、またはツリーへのオーバーレイ追加が必要な場合に使用してください。系統推定(protein-phylogenyスキルを使用)やドメイン注釈(今後の独立したスキル)には使用しないでください。
querying-indonesian-gov-data
インドネシア政府の50以上のAPIとデータソースに接続できます。BPJPH(ハラール認証)、BOM(食品安全)、OJK(金融適正性)、BPS(統計)、BMKG(気象・地震)、インドネシア中央銀行(為替レート)、IDX(株式)、CKAN公開データポータル、pasal.id(第三者法MCP)に対応しています。インドネシア政府データを活用したアプリ開発、.go.idウェブサイトのスクレイピング、ハラール認証の確認、企業の法的適正性の検証、金融機関ステータスの照会、またはインドネシアMCPサーバーへの接続時に使用できます。CSRF処理、CKAN API使用方法、IP制限回避など、すぐに実行可能なPythonパターンを含んでいます。