ReactのuseContextを使って、データをグローバルに扱えるようにしてみます。
サンプルコード
今回はLayer1、Layer2、Layer3というコンポーネントを用意して、Layer1からLayer2、Layer2からLayer3をインポートする例で試してみます。
まずは使用前の例で、propsを使ってデータの受け渡しをしてみます。
Layer1.jsx
import Layer2 from './Layer2.jsx' function Layer1() { const text = "sample"; return ( <div style={{border: "2px solid red", padding: "10px"}}> <p>Layer1</p> <Layer2 text={text} /> </div> ) } export default Layer1
Layer2.jsx
import Layer3 from './Layer3.jsx' function Layer2({text}) { return ( <div style={{border: "2px solid blue", padding: "10px"}}> <p>Layer2</p> <Layer3 text={text} /> </div> ) } export default Layer2
Layer3.jsx
function Layer3({text}) { return ( <div style={{border: "2px solid green", padding: "10px"}}> <p>Layer3</p> <p>{text}</p> </div> ) } export default Layer3
このようにpropsを使ってデータの受け渡しができますが、バケツリレーとなるため深い階層のコンポーネントだと記述が助長になりやすいです。
propsの場合のデモページ
useContextを使うことで、コンポーネント間のデータの受け渡しがしやすくなります。
まずLayer1.jsxでcreateContextを使ってコンテクストを作成して、使用したいコンポーネントをコンテクストプロバイダで囲います。
import { createContext } from 'react' import Layer2 from './Layer2.jsx' export const Text = createContext(null); function Layer1() { return ( <Text.Provider value="sample"> <div style={{border: "2px solid red", padding: "10px"}}> <p>Layer1</p> <Layer2 /> </div> </Text.Provider> ) } export default Layer1
今回の場合はTextにコンテクストを作成しているため、使用したいコンポーネント(Layer2)をコンテクストプロバイダ(Text.Provider)で囲い、valueに渡す値を設定しています。
間に入るLayer2.jsxには特に必要な追加はありません。
propsの記述だけ除去しておきます。
import Layer3 from './Layer3.jsx' function Layer2() { return ( <div style={{border: "2px solid blue", padding: "10px"}}> <p>Layer2</p> <Layer3 /> </div> ) } export default Layer2
最後にLayer3.jsxで受け取る設定を追加します。
Layer1からTextをインポートして、useContextで値を取り出します。
import { useContext } from 'react' import { Text } from './Layer1' function Layer3() { const text = useContext(Text); return ( <div style={{border: "2px solid green", padding: "10px"}}> <p>Layer3</p> <p>{text}</p> </div> ) } export default Layer3
これでLayer1からLayer3に直接データの受け渡しができました。
useContextの場合のデモページ
次にuseStateを使って渡したデータの更新を試してみます。
Layer1.jsxを以下のように変更します。
import { createContext, useState } from 'react' import Layer2 from './Layer2.jsx' export const Text = createContext(null); function Layer1() { const [message, setMessage] = useState("sample"); return ( <Text.Provider value={message}> <div style={{border: "2px solid red", padding: "10px"}}> <p>Layer1</p> <input type="text" value={message} onChange={e => setMessage(e.target.value)} /> <Layer2 /> </div> </Text.Provider> ) } export default Layer1
useStateを使った形に変更して、Layer1から値を更新できるようにしました。
useStateを使うデモページ
次はuseContextを複数使用する場合を試してみます。
入れ子で囲うようにするといいようです。
import { createContext, useState } from 'react' import Layer2 from './Layer2.jsx' export const Text = createContext(null); export const Label = createContext(null); function Layer1() { const [message, setMessage] = useState("sample"); const [message2, setMessage2] = useState("test"); return ( <Text.Provider value={message}> <Label.Provider value={message2}> <div style={{border: "2px solid red", padding: "10px"}}> <p>Layer1</p> <input type="text" value={message} onChange={e => setMessage(e.target.value)} /> <input type="text" value={message2} onChange={e => setMessage2(e.target.value)} /> <Layer2 /> </div> </Label.Provider> </Text.Provider> ) } export default Layer1
Layer3.jsxで追加した分も読み込むように変更します。
import { useContext } from 'react' import { Text, Label } from './Layer1' function Layer3() { const text = useContext(Text); const label = useContext(Label); return ( <div style={{border: "2px solid green", padding: "10px"}}> <p>Layer3</p> <p>{text}</p> <p>{label}</p> </div> ) } export default Layer3
これで複数のデータの受け渡しができました。
複数データを使用するデモページ
最後にcreateContextの引数についてですが、これはコンテクストプロバイダ(Text.Provider)がない場合の初期値になります。
Text.Providerをなくして試してみます。
import { createContext } from 'react' import Layer2 from './Layer2.jsx' export const Text = createContext('default'); function Layer1() { return ( <div style={{border: "2px solid red", padding: "10px"}}> <p>Layer1</p> <Layer2 /> </div> ) } export default Layer1
これで試してみると、createContextの引数で設定したdefaultが表示されるのが確認できました。
コンテクストプロバイダがない場合のデモページ
コメントが承認されるまで時間がかかります。