2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactの状態管理を知らないと、画面が増えた瞬間にコードが壊れる話

2
Posted at

※ これは「どこまでAIに任せるか」という線引きの話ではありません。任せていい部分は任せて大丈夫です。ただ、出てきたコードの構造を見分ける知識がないと、あとから壊れやすい形になっていても気づけない、という話です。

1画面のうちは、useStateで何でも動く

未経験の方がAIと一緒にReactアプリを作るとき、最初の1画面は useState だけでだいたい何でもうまくいきます。

function Cart() {
  const [items, setItems] = useState<Item[]>([]);
  // items を使って画面を組み立てる
}

1つのコンポーネントの中で状態を持って、そのコンポーネントの中で表示する。これだけなら迷う要素がありません。

画面が増えた瞬間に、崩れ方が変わる

問題は、同じ items(カートの中身)を、離れた場所にある別のコンポーネントでも使いたくなったときです。

例えば「ヘッダーのカートアイコンにも件数を出したい」となると、items を持っている Cart から、Header まで値を届ける必要があります。あいだにコンポーネントが挟まっていると、こうなりがちです。

// Before: 使わない層にも props を素通りさせる(プロップスドリリング)
function App() {
  const [items, setItems] = useState<Item[]>([]);
  return <Layout items={items} />;
}

function Layout({ items }: { items: Item[] }) {
  // Layout 自身は items を使わないのに、中継のために受け取る
  return (
    <>
      <Header items={items} />
      <Cart items={items} />
    </>
  );
}

function Header({ items }: { items: Item[] }) {
  return <span>カート: {items.length}件</span>;
}

Layout は items を一切使っていないのに、中継のためだけに受け取っています。これがプロップスドリリングと呼ばれる状態です。コンポーネントが2〜3層ならまだ耐えられますが、画面が増えて階層が深くなるほど、「この props、どこから来てどこで使われているんだっけ」を追うコストが上がっていきます。

AIに機能追加を頼んだときも、この構造を知らないまま「カート件数をここにも表示して」とだけ頼むと、AIが同じprops中継の階層を1本増やす形で対応してくることがあります。動きはしますが、直すたびに中継コンポーネントが増えていく状態です。

Context APIで、中継をなくす

Reactには、この中継を省略する仕組みとして Context API があります。

const CartContext = createContext<Item[]>([]);

function App() {
  const [items, setItems] = useState<Item[]>([]);
  return (
    <CartContext.Provider value={items}>
      <Layout />
    </CartContext.Provider>
  );
}

function Layout() {
  // items を受け取る必要がなくなる
  return (
    <>
      <Header />
      <Cart />
    </>
  );
}

function Header() {
  const items = useContext(CartContext); // 中継なしで直接取得
  return <span>カート: {items.length}件</span>;
}

Layout が items を受け取らずに済むようになりました。Header は必要な場所で直接 useContext を呼んで値を取りに行きます。中継の層を追う必要がなくなる分、コンポーネントを見たときに「このコンポーネント自身が何をしているか」が読みやすくなります。

もう1つの罠: 二重管理

Context化したあとにありがちなのが、Providerで管理している値を、子コンポーネント側でも useState にコピーして持ってしまうケースです。

function Cart() {
  const contextItems = useContext(CartContext);
  const [localItems, setLocalItems] = useState(contextItems); // 罠: コピーしてしまう
  // 以降 localItems を更新しても、Contextの本体は更新されない
}

こうなると、片方だけ更新されて表示がズレる、という不具合が起きます。「値はどこか1箇所だけが正とする」を崩さないことが、状態管理で一番大事な原則です。

AIに頼むときに意識すること

機能を追加で頼むとき、「この値をどこで持つか」を先に自分で決めてから頼むと、AIの出力も安定します。

  • 「1つのコンポーネントの中だけで完結する値」→ useState のままでいい
  • 「複数の離れたコンポーネントで共有したい値」→ Contextに上げる、と明示して頼む

このひと言があるだけで、AIが中継propsを増やして対応するか、Contextを使って対応するかが変わることがあります。

まとめ

  • 1画面のうちは useState で困らない
  • 画面が増えて共有したい値が出てきたら、propsの中継(プロップスドリリング)が起き始めるサイン
  • Context APIで中継を省略できるが、子コンポーネント側でコピーして二重管理にしないよう注意する

未経験者向けの講座を運営しています

未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全20セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。

※ Qiita読者の方には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?