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が表示されるのが確認できました。
コンテクストプロバイダがない場合のデモページ
コメントが承認されるまで時間がかかります。