Props Drillingの迷宮:なぜ私たちは「無意味なバケツリレー」に疲弊するのか
やあ。Reactを書いていると、避けて通れないのが「Props Drilling(プロップス・ドリリング)」という名の泥沼だよね。
親コンポーネントで定義したデータを、孫、ひ孫、さらにはその先のコンポーネントへ渡すために、中間コンポーネントが単なる「通過点」としてPropsを定義し続ける。あの作業、正直言って虚しくならないか? 修正のたびに5つも6つもファイルを開いて修正して……。
今日は、この「React界の負債」とも呼べるProps Drillingをどう解釈し、どうスマートに解決すべきか、現場のリアリティを交えて話そうと思う。
—
1. なぜProps Drillingは起きるのか?
そもそも、Reactは「単方向データフロー」という非常に美しい原則を持っている。データは上から下へ流れる。しかし、アプリケーションが成長してコンポーネントツリーが深くなると、この「上から下へ」という単純な構造が、開発者の首を絞める鎖に変わるんだ。
ブラウザの裏側、つまりReactのレンダリングプロセスに視点を移してみよう。Reactはコンポーネントが再レンダリングされる際、基本的にはその子孫コンポーネントも再評価対象になる。Propsが深くまで渡されているということは、途中の「ただパスするだけ」のコンポーネントたちも、データが更新されるたびに不要な再レンダリングの対象になり得る(もちろん`React.memo`で防げることもあるが、それは対症療法だ)。
「データを使わないコンポーネントが、データを知っている」。これがProps Drillingの本質的な設計ミスなんだ。
—
2. 解決策の処方箋:Context APIの「適材適所」
まず一番身近な解決策はContext APIだ。でも気をつけてほしい。「Props Drillingが面倒だからとりあえずContextに入れよう」は、実は最悪の悪手だ。
Contextは「依存関係の注入」であって、「グローバル変数置き場」ではない。頻繁に更新されるデータをContextに入れると、そのContextを購読している全コンポーネントが再レンダリングされる。パフォーマンスを殺す要因になるんだ。
実践:Composition(合成)による解決
Contextに逃げる前に、まずは「Composition」を検討してほしい。これはReactの基本だが、意外と忘れられている。
// 悪い例:Profileコンポーネントがuserをバケツリレーしている
const App = () =>
const Page = ({ user }) =>
const Profile = ({ user }) =>
// 良い例:Compositionを利用する
// Pageがコンポーネントを子要素として受け取ることで、
// 中間層はデータを知る必要がなくなる
const App = () => (
);
const Page = ({ children }) => (
);
この方法なら、`Page`は`user`データを知る必要がない。これが最もクリーンなReactの姿だよ。
—
3. どうしても必要な場合のContext活用術
それでも、テーマ設定やログイン中のユーザー情報のように、アプリ全体で共有すべき「静的なデータ」はContextを使うのが正解だ。
現場でよく使う、TypeScriptで型安全を担保したContextの実装パターンを載せておく。これをテンプレートとして使ってくれ。
import React, { createContext, useContext, ReactNode } from ‘react’;
// 1. 型定義は必ずエクスポートして再利用可能に
type UserContextType = {
id: string;
name: string;
} | null;
const UserContext = createContext
// 2. Providerをコンポーネント化し、ロジックを隠蔽する
export const UserProvider = ({ children, user }: { children: ReactNode, user: UserContextType }) => {
return (
{children}
);
};
// 3. カスタムフックで呼び出し側をシンプルにする
export const useUser = () => {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error(‘useUser must be used within a UserProvider’);
}
return context;
};
これを使えば、`useUser()`を呼ぶだけで、必要なコンポーネントだけがデータにアクセスできる。中間コンポーネントは一切Propsを気にしなくていい。
—
4. シニアからのアドバイス:ライブラリの使い所
もし、これでも管理しきれないほど状態が複雑になったなら、迷わず状態管理ライブラリ(ZustandやTanStack Queryなど)を導入すべきだ。
- TanStack Query (React Query): サーバーからのデータ取得なら、これ一択。キャッシュ管理のおかげで、Props Drillingそのものが不要になる。
- Zustand: クライアントサイドの複雑な状態ならこれ。Contextのような「全再レンダリング」の問題を回避しやすく、ボイラープレートも非常に少ない。
まとめ:アーキテクトの矜持
いいかい、フロントエンド開発において「綺麗なコード」とは、単にコード行数が短いことじゃない。「変更が局所化されていること」だ。
Props Drillingが発生したとき、それは「コンポーネントの責務境界が崩れ始めている」というサインだ。まずはCompositionで解決できないか考え、次にContextの適正な利用、それでもダメならライブラリを検討する。この順序を忘れないでほしい。
技術は常に「手段」だ。その手段が目的(開発効率と保守性)を達成しているか、常に自問自答し続けるエンジニアであれ。また現場で会おう。

コメント