こんにちは!Reactの学習、楽しく進められていますか?
画面を自由に動かせるようになってくると、「おっ、私すごいことできるぞ!」ってワクワクしてきますよね。
でも、少しアプリが大きくなってくると、こんな壁にぶつかりませんか?
「あれ? このデータ、どこで管理すればいいんだっけ……?」
「とりあえず全部一番上の親コンポーネントに持たせておけば安心かな?」
……ちょっと待った!その優しさ、実はアプリを重くする原因になっちゃうかもしれません。
今回は、Reactの現場でプロたちがこっそり、でもめちゃくちゃ大切にしている「状態の局所化(State Colocation)」という原則について、身近なたとえ話を交えながら優しくお話ししますね。難しく考えなくて大丈夫ですよ。一緒にゆっくり紐解いていきましょう!
—
1. 状態(State)って、どこに置くのが正解なの?
Reactを学び始めると、`useState`を使ってデータを保存する方法を覚えますよね。
「ボタンが押されたかどうか」「モーダルが開いているかどうか」といったデータのことです。
ここで初心者の頃にやりがちなのが、「困ったらとりあえず一番上の親(Appコンポーネントとか)に全部の状態を集めちゃえ!」という作戦です。
これ、一見すると「データが迷子にならなくて安全そう」に見えますよね。
でも、身の回りの「お買い物」に例えてみると、ちょっと不都合なことが見えてきます。
🛒 例え話:文房具の「引き出し」はどこにある?
例えば、あなたがデスクで勉強しているとします。
消しゴムを使うたびに、わざわざリビングの「家族全員の共有ロッカー」まで取りに行きますか?
……面倒くさすぎますよね!消しゴムなんて、自分のペンケースの中、あるいは手の届く引き出しに入っているのが一番ラクだし、効率的です。
逆に、リビングのテレビのリモコンや、みんなで使うトイレットペーパーのストックは、家族全員がアクセスしやすい「みんなの場所(グローバル)」にあるべきです。
Reactのコンポーネントもこれと全く同じ。
「そのデータ、本当にアプリ全体で共有する必要ある?」って考えるのがめちゃくちゃ大事なんです。
—
2. 「状態の局所化(State Colocation)」ってなぁに?
難しい言葉が出てきましたが、要するにこういうことです。
> 「状態(データ)は、それを必要とする一番近くのコンポーネントに置こうね」
これが「状態の局所化(State Colocation)」です。
一番上の親から遠く離れた子コンポーネントの、さらに孫くらいの場所でしか使わないデータなのに、親がわざわざ抱え込んで「ハイ、これ使うんでしょ?」とバケツリレーで渡してあげる必要はありません。
局所化をサボるとどうなるの?(現場の悲劇)
すべてを一番上で管理しようとすると、こんな恐ろしいことが起きます。
1. 無駄な再レンダリングの嵐
一番上の親で「文字が一文字入力されたこと」を監視していると、その入力とは全く関係ない画面の端っこにあるパーツまで、文字を入力するたびに「再描画」されてしまいます。アプリがカクつく原因の第1位です。
2. コードが複雑怪奇になる
使ってもいないデータを、親から子へ、子から孫へと渡すための「プロップス(Props)のバケツリレー」が発生し、コードが spaghetti(スパゲッティ)のように絡まってしまいます。
だからこそ、「必要な場所に、必要なだけ置く」が正義なのです。
—
3. 実例で見てみよう!「アコーディオンメニュー」を作ろう
言葉だけだとイメージしにくいので、よくあるWebサイトの部品「アコーディオン(クリックすると開閉するメニュー)」を例に、実際にコードを見てみましょう。
「今、このメニューが開いているかどうか(isE1Open)」という状態は、そのメニュー自身が知っていれば十分ですよね。
❌ やりがちなアンチパターン(すべてを親で管理しちゃう例)
import React, { useState } from ‘react’;
// 親コンポーネント
function App() {
// メニューAが開いているか?メニューBが開いているか?を全部ここで管理しちゃってる
const [isMenuAOpen, setIsMenuAOpen] = useState(false);
const [isMenuBOpen, setIsMenuBOpen] = useState(false);
return (
私のサイト
{/ 状態とそれを変える関数をわざわざプロップスで渡している…面倒臭い! /}
);
}
// 子A
function MenuA({ isOpen, setIsOpen }) {
return (
{isOpen &&
メニューAの中身です!
}
);
}
// 子B(略)
これ、コンポーネントが増えたらどうなるでしょう? `App` の中身が状態管理コードだらけになってパンクしてしまいます。
✨ 状態の局所化を取り入れた美しいコード
それでは、状態をそれぞれのコンポーネントの中に「お引越し(コロケーション)」させてみましょう!
import React, { useState } from ‘react’;
// 親コンポーネントはスッキリ!子たちの事情なんて知ったこっちゃありません。
function App() {
return (
私のサイト
);
}
// メニューAは、自分の開閉状態を自分で持っています(局所化!)
function MenuA() {
const [isOpen, setIsOpen] = useState(false);
return (
{isOpen &&
ここにメニューAの大切な中身が入ります。
}
);
}
// メニューBも完全に独立しています
function MenuB() {
const [isOpen, setIsOpen] = useState(false);
return (
{isOpen &&
ここにメニューBの大切な中身が入ります。
}
);
}
export default App;
どうですか? `App` コンポーネントがすごくシンプルになりましたよね!
`MenuA` も `MenuB` も、自分に必要なデータや関数を自分自身の中に閉じ込めている(カプセル化されている)ので、パーツ単体でどこにでもポイッと再利用しやすくなりました。これが現場で求められる「メンテナブルな設計」の第一歩です。
—
4. でも、例外はあるの?(どこまでを「局所化」すべき?)
「なるほど、じゃあすべての状態を一番小さく細切れにすればいいんだ!」と思ったそこのあなた、素晴らしい着眼点ですが、ちょっと待ってくださいね。
世の中には「どうしても複数の離れたコンポーネントで共有しなきゃいけないデータ」が存在します。
- ユーザーのログイン情報(ヘッダーにも、マイページ側にも、投稿ボタンの横にも表示したい!)
- アプリ全体のダークモード(黒背景)の切り替え設定
- ショッピングカートに入っている商品リスト
こういうデータまで無理やり局所化しようとすると、逆にコードがおかしなことになります。
そのため、こうした「グローバルに共有すべきデータ」には、Reactの標準機能である `Context API` や、世の中にある状態管理ライブラリ(ZustandやReduxなど)の出番となります。
💡 迷ったときの判断基準
1. そのデータ、他の離れた場所でも使う?
- Yes ➔ 共通の親、またはグローバルな管理を検討する
- No(またはこのパーツだけで完結する) ➔ 即座に局所化!(一番近くのコンポーネントに `useState` を置く)
まずはこのシンプルなルールを頭の片隅に置いておくだけで、あなたの書くReactコードの見違えるような美しさに、きっと自分でも驚くはずです。
—
さいごに
Reactの設計に「絶対的な正解」はありませんが、先輩たちが泥臭い現場で失敗を重ねて見つけ出した知恵(プラクティス)の積み重ねが、こうした原則を作っています。
最初は「どこに書くんだっけ?」と迷うこともあるかと思いますが、コードを書くたびに「あ、このデータはここに置いてあげたほうが、この子(コンポーネント)も喜ぶな」なんて、親心のような気持ちで考えてみてください。
あなたのReact学習ライフが、もっと楽しく、もっと自由になりますように。
つまずいたときは、いつでも立ち止まって深呼吸してくださいね。応援しています!

コメント