Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Redux → Recoil → Zustand → useSyncExternalStore...

Redux → Recoil → Zustand → useSyncExternalStore: 状態管理の10年とReact本来の姿

2025/11/15 に JSConf.jp 2025 Pre Event で発表した登壇資料です。
https://nodejs.connpass.com/event/371397/

株式会社ZOZO
計測プラットフォーム開発本部
計測システム部 フロントエンドブロック
林 恭央

#jsconfjp

Avatar for ZOZO Developers

ZOZO Developers PRO

November 15, 2025
Tweet

More Decks by ZOZO Developers

Other Decks in Technology

Transcript

  1. © ZOZO, Inc. 株式会社ZOZO 計測プラットフォーム開発本部 計測システム部 フロントエンドブロック 林 恭央 2021年12月中途で株式会社ZOZOに入社。

    計測プロダクトのフロントエンド開発に従事。 ZOZOMAT計測で靴のサイズは26.5。 ZOZOGLASSでは【濃いめトーンのクールカラー】判定。 2
  2. © ZOZO, Inc. 4 https://zozo.jp/zozoglass/ • 自宅で簡単・高精度にご自身の顔の肌の色を計測できる フェイスカラー計測ツール • ECにおけるコスメ購入時の課題であった「色選び」に

    関する不安や悩みを解消 • 肌の色を構成する成分、ヘモグロビン量とメラニン量を 画像から推定 • コスメ専門モール「ZOZOCOSME」で取り扱うベース メイクの一部に対応 • 計測者数140万人を突破(2025年9月末時点)
  3. © ZOZO, Inc. 7 Redux時代:明示的フローと制御の厳格さ • 状態を「一元管理」する思想 • Action・Reducer・Storeによる副作用の明確化 •

    メリット:予測可能性/ツールチェーン(Redux DevTools) • デメリット:ボイラープレート/過剰な構造
  4. © ZOZO, Inc. 8 状態を「一元管理」する思想 Reduxは「全てのアプリケーション状態を単一のStoreで管理する」という設計思想を持 つ。 このStoreはアプリ全体の状態をツリー構造で保持し、どのコンポーネントからも参照で きる唯一の情報源( Single

    source of truth)となる。 これにより、 以下の項目が実現された。 • どこに何の状態があるのかが一意に定まり • 複数コンポーネント間の状態同期によるバグが減少 • Undo/Redoやデバッグの容易化
  5. © ZOZO, Inc. 9 Action・Reducer・Storeによる副作用の明確化 状態変更は必ずAction→Reducer→Storeの流れで行われる。 • Action...「何をしたか」の定義(ex. INCREMENT) •

    Reducer...Actionと現在のstateから新しいstateを返す関数(純粋関数として実装) • Store...Reducerで返された新しいstateを保持 この明示的な流れのおかげで、副作用・状態変更の発生箇所が限定され、予測可能な プログラムが書けるようになる。
  6. © ZOZO, Inc. 10 Reduxの実装例 const INCREMENT = 'INCREMENT'; const

    increment = () => ({ type: INCREMENT }); function counterReducer(state = 0, action) { switch(action.type) { case INCREMENT: return state + 1; default: return state; } } このようにReduxは「状態管理をあえて厳格に一元化」し、「副作用を純粋関数により明示化」したことで、複雑な UIの信頼性・可観測性を高めたが、同時に開発効率面で課題も生んだ。
  7. © ZOZO, Inc. 13 atom/selectorsの思想とReact Suspenseとの統合 Selectorはatomなど複数の状態を合成・派生させるための“導関数”。 • 他のatom/selectorの値から動的に計算結果を返すことで、状態の依存関係や再 計算コストが最小化される

    • Selectorのget関数は純粋関数で記述され、依存グラフを自動で最適化する Suspenseと統合することで、非同期の状態データ(例:APIフェッチ)が自然にUIと連携 できる。 → “ローディング”/“エラー”表示がReact標準の機能に組み込まれ、体験とコードが一致 する。
  8. © ZOZO, Inc. 14 atom/selectorsの思想とReact Suspenseとの統合 import { selector }

    from 'recoil'; const filteredBookListState = selector({ key: 'FilteredBookList', get: ({get}) => { const list = get(bookListState); // フィルタ条件による派生例 return list.filter(item => item.available); } });
  9. © ZOZO, Inc. 15 パフォーマンスよりも「React的思考」に寄せたデザイン Recoilは「状態がどこから発生し、どのコンポーネントが依存しているか」 を、Reactの 再レンダリング・依存管理モデルと一体化させて設計した。 • 必要な部分だけ再描画し、不必要な再レンダリングを回避

    • Reduxなどの“厳格な構造”ではなく、「Hooksで直感的に使えるAPI」 • イミュータブルな値の流通や依存性を抽象的に“宣言”できる React公式の思考に沿うことで、複雑なアプリほど「何が変わると何が動くか」がわかり やすく、スケールしやすい実装が可能になった。
  10. © ZOZO, Inc. 16 Zustandの出現:軽量・関数的・フック一発 • Reduxの“重さ”への反発 • create/store による極小API

    • Immerベースによる更新の簡潔さ • server components時代への自然な接続
  11. © ZOZO, Inc. 18 create/store による極小API import { create }

    from 'zustand'; const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), reset: () => set({ count: 0 }) })); // どこからでも const { count, increment } = useStore(); • Contextをラップ不要: ProviderやReducerも不要 • Hooksだけで完結: 必要なstateを必要な場所で呼ぶスタイル • 最小構成: 1ファイルで十分に稼働し、状態の配置も任意 この「極小API」のため、導入・学習コストも非常に低い
  12. © ZOZO, Inc. 19 Server Components時代への自然な接続 React 18以降で公式に導入された useSyncExternalStore は、Zustandでも内部

    的に利用されている。 また、React Server Component により、UI の一部がサーバーでレンダリングさ れるようになった。 これにより、サーバーコンポーネントとクライアント側の状態管理との橋渡しが 保証され、SSR と CSR を意識せずに安全に利用できるようになっている。 • クライアント側でインタラクティブな状態を 安全&シンプルに管理する方法が重要に • 外部ストアとの同期もReact標準に準拠
  13. © ZOZO, Inc. 21 React 18 で導入された公式インターフェース • useSyncExternalStoreはReact 18で公式APIとして追加されたフック。

    • ReactコンポーネントがReact自体の管理外にある外部ストア(Reduxや Zustand、ブラウザAPIなど)と安全・整合的に同期できる仕組みを標準化。
  14. © ZOZO, Inc. 22 外部ストアとの同期を標準化する意義 • 従来、外部ストアの変更検知や再描画には独自の仕組みや回避策 (useEffect, subscribe/unsubscribe)が必要だった。 •

    useSyncExternalStoreの3つの引数で、「購読(subscribe)」「スナップ ショット取得(getSnapshot)」「SSR・Hydration初期値 (getServerSnapshot)」をReactと外部ストア共通のインターフェースと して扱える。 • これにより、状態の正確な同期・予測可能な再レンダリングが保証される。
  15. © ZOZO, Inc. 23 外部ストアとの同期を標準化する意義 import { useSyncExternalStore } from

    'react'; // 外部ストア例 function useCustomStore() { return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }
  16. © ZOZO, Inc. 24 Redux・Zustand・Jotaiなどが同APIで統一される流れ • Redux, Zustand, Jotaiなど近年の主流状態管理ライブラリは内部実装で useSyncExternalStoreを利用し、React標準APIとの親和性が大幅に向上し

    た。 • 全ての外部ストアが同じAPIで「状態の一元的な参照・更新」ができる時代 に。 • サーバーコンポーネント/クライアントコンポーネント間のhydration・同 期も安全に一貫性を保てる。
  17. © ZOZO, Inc. 25 まとめ:Reactの“本来の姿”とは何か • Reactの本質は「宣言的・同期的なUIファースト」。 人間との対話を最適化するためのUI記述言語であり、状態管理はその同期と 安全性を支える役割へと収束した。 •

    状態はUIと分離されるのではなく「同期される対象」 • useSyncExternalStore が示す “minimal but canonical” な方向性 Reactは「UI構築に集中するためのプラットフォーム」。 その進化の中で状態管理は 「外部同期を最小限かつ公式に担保する」姿へと到達した。