Kodokon kodokon.com

state を引き上げてデータを共有する

各 state を最も近い共通の祖先に置くことで、重複させることなく複数のコンポーネント間でデータを共有します。

7 分 · 3 問

このレッスンを Kodokon で開く

2 つの兄弟コンポーネントが同じデータを表示する必要があります。例えば、検索フィールドとそれがフィルタリングするリストです。それぞれのローカル state では機能しません。互いに見ることができないからです。解決策は、state を引き上げること、つまり最も近い共通の祖先へ移すことです。親がデータを所有し、props を通じて下へ渡します。

JSX
import { useState } from "react";

function ProductPage({ products }) {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBar query={query} onChange={setQuery} />
      <ProductList products={products} query={query} />
    </>
  );
}
親が query を所有し、両方の子と共有します。

すると子は制御されたコンポーネントになります。値を読み取り専用で受け取り、それを変更するには親が提供するコールバック関数を呼び出します。データは props を通じて下へ流れ、変更の要求は関数を通じて上へ流れます。この一方向の流れが、アプリケーションの振る舞いを追いやすくします。

JSX
function SearchBar({ query, onChange }) {
  return (
    <input
      value={query}
      onChange={(e) => onChange(e.target.value)}
      placeholder="Search for a product"
    />
  );
}

function ProductList({ products, query }) {
  const visible = products.filter((p) =>
    p.name.toLowerCase().includes(query.toLowerCase())
  );
  return (
    <ul>
      {visible.map((p) => <li key={p.id}>{p.name}</li>)}
    </ul>
  );
}
SearchBar が変更を要求し、ProductList がそれを反映します。

すべてのデータは唯一の信頼できる情報源を持たなければなりません。2 つのコンポーネントがそれぞれ同じ state の自分用のコピーを保存していると、コピーはいずれ食い違います。一方は更新され、もう一方はされず、インターフェイスが一貫しなくなります。state を引き上げれば、そのリスクは取り除かれます。データが多くの階層をまたがなければならない場合、コンポジション(次のレッスン)、そして context が props の受け渡しを軽くします。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. 2 つの兄弟コンポーネントが必要とする state はどこに置くべきですか?
    • 2 つの兄弟のうち最初の方に置き、それがもう一方へ渡す
    • 最も近い共通の祖先に
    • React の外で宣言したグローバル変数に
    • 2 つの兄弟のそれぞれに置き、手動で同期させる
  2. 子コンポーネントは、親が所有する state をどうやって変更できますか?
    • 受け取った prop を直接変更することで
    • 親が prop として渡したコールバック関数を呼び出すことで
    • 親と同じ初期値で useState を使うことで
  3. なぜ同じデータを 2 つの異なる state に保存するのを避けるべきですか?
    • メモリを使いすぎるから
    • React は同じ初期値の useState を 2 回呼ぶことを禁じているから
    • 2 つのコピーはいずれ同期がずれ、インターフェイスが一貫しなくなるから