はじめに
こんにちは、Tunです。
今回は、Stateの管理について深堀していきたいと思います。
stateの管理問題
Reactにおいて、コンポーネントの構成が5階層の場合、一番上の階層のコンポーネントで定義しているstate
を一番下の階層のコンポーネントに渡したい場合は、props
で渡すことができます。
しかし、この方法では問題点が多く、バケツリレーのように複数のコンポーネントにまたがってstate
の受け渡しが行われ、コードの複雑化や、再利用不可のコンポーネント、中間コンポーネントにおける無駄なprops
の受け取り、さらには一番下の階層のコンポーネントにstate
を渡そうとすると、全てのコンポーネントが再レンダリングされるというデメリットがあります。
この、無駄なコードや挙動を減らすためにstate
の管理方法を考えなければならないというわけです。
Global stateの管理方法
useContext
以外にもReduxやRecoil、Apollo Clientなどがありますが、今回はContext
の解説のみで、別の記事で他の管理方法を書きたいと思います。
Context
上記のstate
の管理問題を解決してくれるオーソドックスな方法は、Reactが提供しているContext
を使うことです。
-
React.createContext
でContext
の器を作成する - 作成した
Context
のProvider
でGlobal stateを扱いたいコンポーネントを囲む -
state
を参照したいコンポーネントでReact.useContext
を使う
上記の手順で、GlobalなState
管理が可能になります。
詳しく解説
1. まずはContext
を保持するためのプロバイダーコンポーネントをExampleProvider.jsx
という名前で、providersというフォルダの配下に作成します。
import { createContext } from "react";
export const ExampleContext = createContext({});
これでContext
の器の作成が完了しました。createContext
の引数にはデフォルト値が設定でき、上記の例は空のオブジェクトが初期値になっています。
2. Context
の器を作成しただけでは使えるようにはなりません。Provider
でContextの値を参照したいコンポーネント群を囲む必要があります。
import { createContext } from "react";
export const ExampleContext = createContext({});
export const ExampleProvider = props => {
const {children} = props;
//動作確認のためのオブジェクト
const sampleObj = { sampleObj : "test" };
//ExampleContextの中にProviderがあるのでそれでchildrenを囲む
//valueにはグローバルに扱う実際の値を設定する
return (
<ExampleContext.Provider value={sampleObj}>
{children}
</ExampleContext.Provider>
);
};
Provider
コンポーネントはなんでも囲めるようにPropsとしてchildrenを受け取るようにするのがポイントです。このProvider
というコンポーネントにはvalue
というPropsを設定することができ、ここにグローバルに管理する実際の値を設定します。
import ReactDOM from "react-dom";
import { App } from "./App";
import { ExampleProvider } from "./components/providers/ExampleProvider";
ReactDOM.render(
<ExampleProvider>
<App />
</ExampleProvider>,
documenet.getElementById("root")
);
これで、Provider周りの準備が整いました。
3.実際にContextの値を参照してみましょう。
import { useContext } from "react";
import { ExampleContext } from "./components/providers/ExampleProvider";
const contextValue = useContext(ExampleContext);
console.log(contextValue); //{sampleValue : "test"}
まず、ReactからuseContext
をimport
し、作成したExampleContext
もimportします。importしたuseContext
の引数に参照するContextの値を指定します。
このようにContextの値を使用するコンポーネント側では、useContextを使いその引数に対象のContextを指定するだけで参照できます。
再レンダリングに注意
Contextオブジェクトの値が何か更新されたときは、useContextでそのContextを参照しているコンポーネントは全て再レンダリングされます!
なので、1つのContextに属性の異なるいろんなStateを詰め込むのは避けましょう。
次回はTypeScriptに軽く触れたいと思います