こんにちは!Reactの世界へようこそ。チーフアーキテクトの私です。
Reactを触り始めてすぐの頃、「あれ?さっき書き換えたはずなのに、画面のあっち側で値が古いままになっている…」「親と子でデータを共有したいのに、どうやったらいいんだっけ?」という壁にぶつかったことはありませんか?
大丈夫ですよ、まったく心配いりません。ここを通らずしてReact使いになった人はいませんからね。誰もが最初に迷うところです。
今日は、そんなモヤモヤを綺麗に解消してくれるReactの超重要テクニック「状態の持ち上げ(Lifting State Up)」について、身近な例えを交えながら、一緒に紐解いていきましょう。
—
1. そもそも「状態(State)」ってなに?
Reactにおける`useState`などの「状態」は、いわば「コンポーネントが自分専用に持っているメモ帳」だと思ってください。
ボタンが何回押されたか、今入力されている文字は何か。そうした「画面の中で変化するデータ」をこのメモ帳に書いて記憶しておき、データが変わるたびにReactが自動で画面を最新の状態に描き直してくれます。
だけど、こんな「困ったこと」ありませんか?
例えば、画面の中に「アコーディオンメニュー」と「閉じるボタン」という、別々のパーツ(コンポーネント)があったとします。
- アコーディオンパーツ:「今、自分が開いているかどうか」を知りたい
- 閉じるボタンパーツ:「今、アコーディオンが開いているから、押されたらそれを閉じたい」と命令したい
ここで、それぞれのパーツが別々のメモ帳(`useState`)を持っていたらどうなるでしょうか?
お互いのメモ帳の中身を見せ合うことができず、「あっちのボタンを押したのに、こっちのメニューが閉じてくれない!」という噛み合わない現象が起きてしまいます。これが、コンポーネント間のデータ共有で私たちが最初につまずく罠です。
—
2. 身近な例え:「家族みんなのリビングのホワイトボード」
この問題を解決するのが「状態の持ち上げ(Lifting State Up)」です。
想像してみてください。
あなたと家族がそれぞれ自分の部屋(=子コンポーネント)に小さなメモ帳を持っているとします。「今日の夕飯は何にする?」という情報を共有したくても、お互いの部屋を行き来しないと分かりませんよね。
そこでどうするか?
「みんなが見えるリビングの真ん中(=親コンポーネント)に、大きめのホワイトボードを置く」のです。
- 夕飯のメニューを変えたくなったら、リビングのホワイトボードに書きに行く(親の状態を更新する)
- 家族みんながリビングのホワイトボードを見ることで、常に最新の夕飯のメニューを知ることができる(親の状態を子に渡す)
Reactの設計もこれとまったく同じです。バラバラの場所にある状態は、一番近い「共通の親」の場所まで引き上げる(持ち上げる)。これが「状態の持ち上げ」の正体です。
—
3. 実践!コードで見てみよう
言葉だけだと少しフワッとするので、実際に動かせるコードで見てみましょう。
今回は、「親コンポーネントから、2つの子コンポーネントに状態を共有し、どちらからでも値を操作できるようにする」というよくあるシチュエーションを書いてみます。
エディタにそのまま貼り付けて動きを確認できるように作ったので、ぜひ試してみてくださいね。
import React, { useState } from ‘react’;
// ==========================================
// 1. 子コンポーネント:文字を入力するパーツ
// ==========================================
function InputForm({ text, onTextChange }) {
return (
① 入力パーツ(子)
onTextChange(e.target.value)}
placeholder=”ここに文字を入力してね”
/>
);
}
// ==========================================
// 2. 子コンポーネント:入力された文字を表示するパーツ
// ==========================================
function DisplayText({ text }) {
return (
② 表示パーツ(子)
{/ 親から受け取った最新のテキストを表示するよ /}
現在の値: {text ? text : ‘(まだ何も入力されていません)’}
);
}
// ==========================================
// 3. 親コンポーネント:すべてをまとめるリビング
// ==========================================
export default function App() {
// ★ここに「状態(メモ帳)」を置く!これが「持ち上げられた状態」です。
const [sharedText, setSharedText] = useState(”);
return (
お父さん(親コンポーネント)のリビング
ここで状態(sharedText)を一元管理しています。
{/ 入力パーツに、現在の値と、値を更新する関数を渡す /}
{/ 表示パーツに、現在の値を渡す /}
);
}
コードのポイント
1. 親(`App`)が `const [sharedText, setSharedText] = useState(”);` という「ホワイトボード(状態)」を持っています。
2. その状態と、それを書き換えるための関数(`setSharedText`)を、それぞれの子(`InputForm` と `DisplayText`)にプロパティ(props)として渡しています。
3. 子A(入力フォーム)で文字が打たれると、親の `setSharedText` が呼ばれて親のメモ帳が書き換わります。
4. 親のメモ帳が書き換わると、Reactが自動的に子B(表示パーツ)も含めて画面を再描画するため、「入力した瞬間にもう片方のパーツの表示が変わる」という連携プレイが完成します。
—
4. チーフアーキテクトからのアドバイス
実務の現場でコードを書いていると、「あれ、この状態はどこに持たせるのが正解なんだっけ?」と手が止まることがよくあります。迷ったときは、次のルールを思い出してください。
> 「そのデータを使いたいコンポーネントたちの、一番すぐ上にいる共通の親に置く」
これだけです。むやみやたらに一番上の親(Appのさらに上など)に何でもかんでも持たせようとすると、コードの見通しが悪くなり(いわゆる“プロップスバケツリレー”地獄になります)、パフォーマンスの低下を招くこともあります。
「共有したい仲間たちのすぐ上の親へ、そっと持ち上げる」。
この基本の型さえ身につければ、複雑なUI設計も怖くありません。
最初は頭がこんがらがることがあるかもしれませんが、手を動かしてブラウザの画面がスッと連動したときの「おっ、動いた!」という感動は、何ものにも代えがたい楽しさです。
あなたのReactの旅が、素晴らしいものになりますように。
焦らず、一歩ずつ進んでいきましょう!

コメント