状態を管理するためのReactコンテキストの使い方
Reactのコンテクストは、繰り返しのプロップドリリングを排除するのに役立つ機能です。プロップはしばしば親コンポーネントから深く入れ子になった子コンポーネントに渡されます。これには、各中間コンポーネントがツリー内の次のコンポーネントにプロップを「転送」する必要があります。
コンテキストを使用すると、ツリーを通じてプロップを手動で各レベルに転送することなく渡すことができます。これは、トップレベルのアプリケーション状態を表すデータに特に便利です。ユーザー設定、認証トークン、およびAPIレスポンスからキャッシュされたデータは、Context APIの良い候補です。
このデータは、しばしばReduxのような専用の状態ストアによって管理されます。代わりに、Reactのコンテキストを使用することもできます。これにより、Reduxがアプリケーションレベルの状態を保持するだけの場合のアプリ内の複雑さが大幅に削減されます。アクション、ディスパッチャー、およびリデューサーを作成する代わりに、Reactのコンテキストを使用することができます。
文脈のない例
ユーザーがログインした後、アプリの状態にユーザーの名前やメールアドレスなどの重要なデータを保存したくなることがよくあります。これにより、各コンポーネントはAPIサーバーへの往復を行うことなく、ユーザーに関する情報を表示できます。
これを実装するための素朴な方法は次のとおりです:
const UserProfileLink = ({user}) => {
return (
<div>
<p>Logged in as {user.Name}</p>
<a href="/logout">Logout</a>
</div>
);
};
const Header = ({user}) => {
return (
<div>
<h1>My App</h1>
<UserProfileLink user={user} />
</div>
);
}
const App = () => {
const [user, setUser] = useState({
Name: "Foo Bar",
Email: "foobar@example.com"
});
return <Header user={user} />
}
アプリには3つのコンポーネントがあります。最上位の
Appコンポーネントは、その状態内で現在のユーザーを追跡します。それは以下をレンダリングします
Headerコンポーネント。
Headerレンダリング
UserProfileLinkユーザーの名前を表示します。
重要なのは、ただ
UserProfileLink実際にユーザーオブジェクトと対話します。
Headerまだ受け取らなければならない
userプロパティですが。これはそのままに転送されます。
UserProfileLink消費されることなく
Headerこのアプローチの問題は、コンポーネントツリーが複雑になるにつれて悪化します。中間のコンポーネントが自分自身でプロプスを使用しなくても、複数のネストされたレベルを通じてプロプスを渡すことになるかもしれません。
コンテキストAPIの追加
これらの問題は、Contexts APIを使用して軽減できます。上記の例をリファクタリングして削除することができます
userプロップから
Headerその
Appコンポーネントは現在のユーザーオブジェクトを保存するための新しいコンテキストを作成します。
UserProfileLinkコンテキストを消費します。コンポーネントは、コンテキストによって提供されるユーザーデータにアクセスできるようになります。これは、コンポーネントをReduxストアに接続するのと似た概念です。
const UserContext = React.createContext(null);
const UserProfileLink = () => {
const user = useContext(UserContext);
return (
<div>
<p>Logged in as {user.Name}</p>
<a href="/logout">Logout</a>
</div>
);
};
const Header = () => {
return (
<div>
<h1>My App</h1>
<UserProfileLink />
</div>
);
}
const App = () => {
const [user, setUser] = useState({
Name: "Foo Bar",
Email: "foobar@example.com"
});
return (
<UserContext.Provider value={user}>
<Header />
</UserContext>
);
}
このリファクタリングされたコンポーネントのセットは、コンテキストの使い方を示しています。
そのプロセスは、利用可能にしたい値の新しいコンテキストを作成することから始まります。呼び出しは
React.createContext(null)デフォルト値を持つ新しいコンテキストを設定します
nullその
Appコンポーネントはラップするように書き直されました
Headerの中で
UserContext.Providerその
valueプロパティはコンテキストの現在の値を決定します。これは次のように設定されます
user状態に保持されているオブジェクト。コンテキストプロバイダーの下にネストされたコンポーネントは、今やコンテキストを消費し、ユーザーオブジェクトにアクセスできるようになります。
プロバイダーインスタンスの中にネストされていないコンポーネントからコンテキストにアクセスしようとすると、そのコンポーネントは渡したデフォルト値を受け取ります。
createContext()これは一般的に避けるべきですが、決して変わらない静的なコンテキスト値については例外です。
提供されたコンテキスト値へのアクセスが観察されます
UserProfileLinkその
userプロパティが削除されました。このコンポーネントは、ReactのuseContext()フックを使用して、現在の値を取得します。
UserContext. これにより、ユーザーオブジェクトが提供されます。
Appコンポーネント!
最終的な変更は
Headerコンポーネント。これ以上転送する必要はありません。
userそれを完全に取り除くことができるプロップ。実際、
user小道具はアプリ全体から消えました。現在は次のように供給されています。
App中へ
UserContextプロバイダー、特定のコンポーネントではありません。
クラスコンポーネントでのコンテキストの使用
これまでのところ、私たちは関数コンポーネント内のコンテキストのみを使用してきました。コンテキストはここでうまく機能します。
useContext()フックは子コンポーネント内で提供されたデータにアクセスすることを簡素化します。
クラスコンポーネントでもコンテキストを使用できます。推奨される方法は、staticを設定することです。
contextType使用したいコンテキストインスタンスへのクラスプロパティ。Reactはこのプロパティを読み取り、設定します。
contextコンポーネントインスタンスのプロパティを、コンテキストによって提供された現在の値に設定します。
const UserContext = React.createContext({Name: "Foo Bar"});
class MyComponent extends React.Component {
// Tell React to inject the `UserContext` value
static contextType = UserContext;
render() {
// Requested context value made available as `this.context`
return <p>{this.context.Name}</p>;
}
}
別のアプローチは、コンポーネントの子要素をコンテキストの「コンシューマー」の中でレンダリングすることです。各コンテキストのコンシューマーにアクセスすることができます。
Consumerコンテキストインスタンスのプロパティ。
コンシューマーの子供として関数を提供する必要があります。関数はコンポーネントがレンダーされるときにコンテキストの値で呼び出されます。
これがどのように見えるかです:
const UserContext = React.createContext({Name: "Foo Bar"});
class MyComponent extends React.Component {
render() {
return (
<UserContext.Consumer>
{user => <p>{user.Name}</p>}
</UserContext.Consumer>
);
}
}
使用する
contextType各コンポーネントに対して1つのコンテキストに制限します。コンテキスト消費者はこの問題に対処しますが、あなたのコンポーネントのレンダーメソッドをより不透明にする可能性があります。どちらのアプローチも、まったく簡単ではありません。
useContext()関数コンポーネントに利用可能なフック。
コンテキスト値の更新
コンテキストの値は、プロップスと同様に機能します。子コンポーネントがコンテキストの値を更新する必要がある場合は、関数をコンテキストに追加します。子コンポーネントは、その関数を呼び出してコンテキストの値の変更を実行できます。
const UserContext = React.createContext(null);
const UserProfileLink = () => {
const context = useContext(UserContext);
return (
<div>
<p>Logged in as {context.user.Name}</p>
<a onClick={() => context.logoutUser()}>Logout</a>
</div>
);
};
const Header = () => {
return (
<div>
<h1>My App</h1>
<UserProfileLink />
</div>
);
}
const App = () => {
const [user, setUser] = useState({
Name: "Foo Bar",
Email: "foobar@example.com"
});
const contextValue = {
user,
logoutUser: () => setUser(null)
}
return (
<UserContext.Provider value={contextValue}>
<Header />
</UserContext>
);
}
この改訂された例はどのように示していますか
App今や提供されています
logoutUserコンテキスト内の関数。コンテキストの利用者は、この関数を呼び出して更新できます。
user中で
Appコンポーネントの状態が変更され、それに応じてコンテキストの値が修正されます。
再レンダリングの管理
Reactはプロバイダーのすべての子コンポーネントを再レンダリングします。
valueプロパティの変更。値の変更は使用して比較されます。
Object.is()つまり、コンテキストプロバイダーとしてオブジェクトを使用する際には注意が必要です。
valueconst App = () => {
return (
<ExampleContext.Provider value={{foo: "bar"}}>
<NestedComponent />
</ExampleContext.Provider>
);
}
このコンポーネントは再レンダリングされます
NestedComponent毎回
Appレンダリング。プロバイダーのために新しいオブジェクトインスタンスが作成されます。
value各タイムごとにpropを変更することで、Reactはコンポーネントの子要素を再レンダリングします。
これを持ち上げることで対処できます。
valueオブジェクトをコンポーネントの状態に変換します。これにより、毎回同じオブジェクトがレンダリングされることが保証されます。
const App = () => {
const [obj, setObj] = useState({foo: "bar"});
return (
<ExampleContext.Provider value={obj}>
<NestedComponent />
</ExampleContext.Provider>
);
}
要約
Reactのコンテキストは、データをコンポーネントツリーに渡すための一級の方法を提供することで、プロップのドリリングを回避するのに役立ちます。コンテキストは、Reduxのような状態管理ライブラリの良い代替手段です。それらはReactに組み込まれており、Reduxが深くネストされたコンポーネントを共有状態源に接続するためだけに使用されるプロジェクトの間で注目を集めています。
コンテキストは、アプリケーション内で保持される「グローバル」データを意図しています。また、アプリの特定のサブセクションのデータを保持することもできます。コンテキストは、ローカルなコンポーネントの状態を完全に置き換えることを目的としていません。個々のコンポーネントは、データが浅くネストされたツリーを上下に渡るだけの場合には、状態やプロパティを引き続き使用するべきです。
