Reactで兄弟コンポーネントの状態を共有する
検索欄に入力した文字列を、別の一覧コンポーネントでも使いたい場合があります。兄弟同士で別々にstateを持つと値がずれるため、共通の親にstateを置きます。
親で値を管理する
import { useState } from 'react';
type SearchInputProps = {
value: string;
onChange: (value: string) => void;
};
const SearchInput = ({ value, onChange }: SearchInputProps) => (
<input value={value} onChange={(event) => onChange(event.target.value)} />
);
const ResultCount = ({ count }: { count: number }) => <p>{count}件</p>;
export const ProductSearch = () => {
const [query, setQuery] = useState('');
const products = ['ノート', 'ペン', '消しゴム'];
const filtered = products.filter((name) => name.includes(query));
return (
<>
<SearchInput value={query} onChange={setQuery} />
<ResultCount count={filtered.length} />
<ul>{filtered.map((name) => <li key={name}>{name}</li>)}</ul>
</>
);
};入力欄は値を受け取り、変更を親へ通知します。一覧と件数は親が持つ同じ値から計算されます。
filteredはqueryとproductsから計算できるので、別のstateへ保存しません。両方をstateにすると、入力値だけ更新して絞り込み結果の更新を忘れるなど、値が食い違う経路が増えます。
どこまで持ち上げるか
stateは、それを必要とするコンポーネントに最も近い共通の親へ置きます。アプリ全体で使わない値までグローバル状態にする必要はありません。
例えば検索欄の開閉だけをSearchInputが使うなら、その開閉状態は子の中に残して構いません。共有したい値だけ持ち上げると、親の責務が増えすぎません。子の数が増えてpropsを何層も中継するようになってから、Contextなどを検討します。