本文へスキップ
BecomeCoder

Reactコース · 第4章 useEffectとこれから · レッスン13

状態を上げる ― 兄弟コンポーネントでstateを共有する

ブラウザで完結

導入

これまで state は 1 つのコンポーネントの中に持っていました。でも実務では「入力欄に打った文字を、別の場所のプレビューにも反映したい」のように、複数のコンポーネントで同じ値を共有したい場面がたくさんあります。

React には兄弟どうしで直接データを渡す仕組みはありません。そこで使うのが定番テクニック 「状態を上げる(Lifting State Up)」 です。合言葉は 「stateは、それを使う全員の”共通の親”に置く」

説明

共有したい値(text)をに持たせ、下の図のように配ります。値は props で下へ流し、変更のきっかけ(イベント)は関数を渡して上へ返します。

flowchart TB
  parent["親 App<br/>state: text を持つ(唯一の置き場)"]
  input["子 InputBox<br/>入力する"]
  preview["子 Preview<br/>表示する"]
  parent -->|"text と setText を props で渡す(下へ)"| input
  parent -->|"text を props で渡す(下へ)"| preview
  input -.->|"onChange で setText を呼ぶ(上へ)"| parent

「入力欄」と「プレビュー」が、親の 1 つの state を共有する例です。

// 子①:入力する部品(自分ではstateを持たず、親から受け取って使う)
function InputBox({ text, onChange }) {
  return (
    <input
      value={text}
      onChange={(e) => onChange(e.target.value)}
      placeholder="なにか入力"
    />
  );
}

// 子②:表示する部品(同じ値を受け取って映すだけ)
function Preview({ text }) {
  return <p>プレビュー: <strong>{text || "(まだ空です)"}</strong></p>;
}

// 親:state はここ 1 か所だけ。子ふたりに配る。
function App() {
  const [text, setText] = React.useState("");

  return (
    <div style={{ fontFamily: "system-ui", maxWidth: 320 }}>
      <h2>状態を上げる</h2>
      <InputBox text={text} onChange={setText} />
      <Preview text={text} />
      <p>文字数: {text.length}</p>
    </div>
  );
}
  • text を持つのは App だけInputBoxPreview も自分では state を持ちません。
  • 親は text(値)と setText(更新関数)を props で下へ渡します。
  • 入力があると InputBoxonChange(=親の setText)を呼び、更新を 上へ伝えます。
  • state が変わると、それを使っている InputBoxPreview・文字数表示がまとめて最新になります。

これが「単方向データフロー」――データは上から下へ、変更の通知は下から上へという、React の一番大事な考え方です。

やってみよう

  • Preview をもう 1 つ増やしても、同じ text を渡すだけで両方が同期することを確かめましょう。
  • 大文字に変換して表示する <p>大文字: {text.toUpperCase()}</p>App に足してみましょう。1 つの state から、いくつでも表示を作れます。

実際に動かしてみよう

下のエディタにReactのコードを書くと、右側のプレビューにその場で描画されます。`function App() { ... }` を定義すると自動で表示されます(React・useState などは React.useState の形で使えます)。本文の例を打ち込んだり書き換えたりして、表示の変化を確かめながら進めましょう。

React — ブラウザ内で実行

JSXをその場で変換して描画する学習用React環境(React18 + Babel・同梱)を読み込みます。
スクロールして表示された時点でも自動で読み込まれます。