こんにちは!Reactの学習、楽しく進めていますか?
「画面に文字を表示できるようになってきたぞ!」「ボタンを押したらカウントが増える!」と感動したのも束の間、「あれ、このコンポーネントにあるデータを、あっちのコンポーネントでも使いたいんだけど……どうすればいいの?」という壁にぶつかっていませんか?
大丈夫ですよ。その悩み、世界中のすべてのReactエンジニアが必ず最初に通る「通過儀礼」ですから安心してください。
今日は、そんなコンポーネント間のデータ共有のモヤモヤをスッキリ解消してくれる、Reactの超基本にして最強の設計パターン「状態の引き上げ(Lifting State Up)」について、身近な例えを交えながらじっくりお話ししていきますね。
—
1. なぜ「状態の共有」で悩むのか?(身近な例え話)
Reactの世界は、まるで「家族の独立した個室」が集まったシェアハウスのようなものです。
基本ルールとして、それぞれの部屋(コンポーネント)にあるプライベートな持ち物(状態・`useState`)は、基本的にはその部屋の外には持ち出せません。
例えば、リビングにある「家族共用のテレビのリモコン」を想像してみてください。
お兄ちゃんの部屋から「リビングのテレビのチャンネルを変えたい!」と思っても、リモコンがリビングのテーブル(親コンポーネント)にポツンと置いてあったら、お兄ちゃんの部屋(子コンポーネント)から直接触ることはできませんよね。
Reactもこれと全く同じです。
兄弟である「子コンポーネントA」と「子コンポーネントB」が、お互いの持っているデータを行ったり来たりさせようとしても、直接やり取りすることはできないルールになっているのです。
じゃあ、どうするか?
答えは簡単。「2人の部屋の真ん中、つまり共通の親であるリビングにリモコンを引っ越させればいい」のです。これがまさに「状態の引き上げ」というわけです。
—
2. 「状態の引き上げ」って具体的にどういうこと?
言葉の通り、子コンポーネントが持っていた `useState`(状態)を、それらを包み込んでいる共通の親コンポーネントへと移動(引き上げ)させるテクニックです。
親に状態を置くことで、親はその状態を管理しつつ、 props(プロパティ)という「おすそ分け」の仕組みを使って、必要な子どもたちにデータや更新用の関数を配給してあげることができるようになります。
—
3. 実践!コードで見てみよう(お買い物アプリの例)
百聞は一見に如かず。よくある「商品の個数を変えるボタン」と「その合計金額を表示するエリア」を例に作ってみましょう。
最初は、やりがちな「あー、データが共有できなくて困った!」という状態から、見事に状態を引き上げて解決するまでの流れをコードで追ってみます。
悪い例:それぞれが勝手に状態を持っている(同期できない)
import React, { useState } from ‘react’;
// 子A:個数を変えるボタン
function Counter() {
const [count, setCount] = useState(1);
return (
現在の個数: {count}個
);
}
// 子B:合計金額を表示したい
function TotalPrice() {
// あ、あれ…?Counterの中の count が見えない!
const pricePerItem = 500;
return
合計: まとめて計算できません!
;
}
export function ShopAppBad() {
return (
お買い物シミュレーター(失敗例)
);
}
これでは、`Counter`の中にある `count` を `TotalPrice` が知るすべがありません。お兄ちゃんの部屋にあるリモコンを弟の部屋から使えないのと同じです。
解決編:状態を親に「引き上げる」!
それでは、親である `ShopAppGood` に `count` の状態を引っ越しさせましょう。
import React, { useState } from ‘react’;
// 子A:個数を変更するボタン(親から「今の数」と「変える機能」をもらう)
function Counter({ count, onIncrement }) {
return (
現在の個数: {count}個
{/ ボタンが押されたら、親から受け取った関数を実行する /}
);
}
// 子B:金額を表示する(親から「今の数」をもらう)
function TotalPrice({ count }) {
const pricePerItem = 500; // 1個500円
const total = count pricePerItem;
return (
合計金額: {total}円
);
}
// 親コンポーネント:ここで状態をがっちり管理する!
export function ShopAppGood() {
// ★ 状態をここに引き上げました!
const [count, setCount] = useState(1);
// 個数を増やすための関数
const handleIncrement = () => {
setCount((prevCount) => prevCount + 1);
};
return (
お買い物シミュレーター(正解例:状態の引き上げ)
{/ Counter に数と関数を props で渡す /}
{/ TotalPrice に数だけを props で渡す /}
);
}
このコードの「なるほど!」ポイント
1. 親が情報の一元管理(シングル・ソース・オブ・トゥルース)になる
`count` という大事なデータは親 (`ShopAppGood`) が一手に引き受けて持っています。そのため、「今いくつだっけ?」というデータ迷子が起きません。
2. 子は受け取った通りに描画して、イベントを親に伝えるだけ
`Counter` は自分で状態を持たず、親から渡された `onIncrement`(「増やしてね」とお願いする関数)を実行しているだけです。これを実務では「制御されたコンポーネント」の第一歩と言ったりします。
—
4. チーフアーキテクトからの実務アドバイス
現場でたくさんのコードレビューをしていると、「あ、ここでまた無理やりReduxやContext APIを使おうとして迷子になってるな」という場面に出くわします。
状態管理が複雑になると、ついつい「何でもグローバルな状態管理ライブラリに入れちゃえ!」となりがちですが、ちょっと待ってください。
兄弟コンポーネント同士でちょっとデータを共有したいだけなら、今日学んだ「状態の引き上げ(Lifting State Up)」で9割は解決します。
「どこに状態を置くべきか迷ったら、そのデータを使いたいコンポーネントたちの『一番近い共通の親』を探して、そこに置く」
これさえ覚えておけば、あなたのReactライフは劇的にスムーズになります。
最初は「親から子へデータを渡し、子から親へは関数(イベント)経由で伝える」というキャッチボールの感覚に少し戸惑うかもしれませんが、手を動かしていくうちに自然と手が勝手に動くようになりますよ。
焦らず、一歩ずつ、あなたのペースで進めていきましょう。応援しています!

コメント