【入門編】 状態の局所化(State Colocation)の原則 – React実践ガイド

こんにちは!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学習ライフが、もっと楽しく、もっと自由になりますように。
つまずいたときは、いつでも立ち止まって深呼吸してくださいね。応援しています!

コメント

タイトルとURLをコピーしました