useContextとは
- 上位コンポーネントで用意された値を、任意の子孫コンポーネントで使用できる仕組み。
- propsのバケツリレーを解消する。
- ただし、上位コンポーネントでのコンテキスト登録が前提となり、再利用性がなくなる。そのため安易に多用はしない。
useContextによるデータ受け渡しの流れ
以下の3ステップを踏む。
- コンテキストの作成
createContext
でコンテキストを作成し公開する。 - コンテキストタグで囲み、値を設定
1.で作成したコンテキストをタグ(<コンテキスト名.Provider>
)として使用し、子コンポーネントを囲む。
その際、propsのvalueに子孫コンポーネントへ受け渡したい値を設定する。 - コンテキスト値の使用
子孫コンポーネント側で、useContext(1.のコンテキスト)
を行うと、設定した値を取得できる。
useContextの使用例
親コンポーネントで設定した値を、孫コンポーネントで使用する例。
stateとその更新関数も受け渡すため、孫コンポーネントで変更したstateは、リアクティブに親コンポーネントのstateにも反映される。
Parent.tsx
import { Dispatch, SetStateAction, createContext, useState } from 'react';
import { Child } from './Child';
type ISampleValue = {
title: string;
value: number;
setValue: Dispatch<SetStateAction<number>>;
};
// 1. コンテキストを作成(と初期化)し公開する。(別ファイルに切り出した方が良いかも。)
export const SampleContext = createContext<ISampleValue>({
title: '',
value: 0,
setValue: () => {},
});
export function Parent() {
const [value, setValue] = useState(0);
// 受け渡す値。propsと同様に、stateの更新関数も受け渡せる。
// 本来は、useReducerを用いて更新する処理は限定したほうがよい。
const providValue = {
title: 'context provider',
value,
setValue,
};
return (
// 2. <コンテキスト.Provider>のタグで子コンポーネントを囲む。
// valueに受け渡す値を設定する。
<SampleContext.Provider value={providValue}>
parent component: {providValue.value}
<Child />
</SampleContext.Provider>
);
}
Child.tsx
import { useContext } from 'react';
import { SampleContext } from './Parent';
// 子孫コンポーネントへpropsは渡していない。(バケツリレーしていない)
export function Child() {
return (
<>
<div>child component</div>
<GrandChild />
</>
);
}
export function GrandChild() {
// 3.useContext(コンテキスト)で、親で設定したコンテキストの値が取得できる。
// 更新関数を使えば、stateの更新も可能。
const { title, value, setValue } = useContext(SampleContext);
return (
<>
grand child component: {title}
<div>
<input
type="text"
value={value}
// state値を更新→親で表示しているstateにもリアクティブに反映
onChange={(element) => setValue(+element.target.value)}
/>
</div>
</>
);
}