unstated-next による Redux に頼らない状態管理の考察

unstated-next による Redux に頼らない状態管理の考察

React.kyoto v0.3.0 でのLT発表資料です。
https://react-kyoto.connpass.com/event/137847/

790f55ccde7a62df8f25747586657090?s=128

Yoshihide Jimbo

July 19, 2019
Tweet

Transcript

  1. unstated-next による
 Redux に頼らない状態管理の考察 React.kyoto v0.3.0 | Jul 19, 2019

  2. 神保 嘉秀 @jmblog じんぼ よしひで

  3. None
  4. 今⽇話すこと • unstated-next の紹介 • 気になる点や個⼈的な所感

  5. unstated-next の紹介

  6. https://github.com/jamiebuilds/unstated-next unstated-next

  7. Hooks の登場によって、 Redux などの「状態管理ライブラリ」に頼らず、 React の Context と Hooks だけで状態管理を実装することが

    可能になった。
 
 unstated-next はその実装をサポートしてくれる、
 必要最⼩限なライブラリ(わずか 200バイト)
  8. unstated-next と unstated の関係 • unstated というライブラリもあってちょっとややこしい。 • 開発者はどちらも @jamiebuilds

    ⽒。 • unstated は Context を活⽤したシンプルな状態管理ライブラリ。
 2018年前半に発表された。 • その後、React から Hooks がリリースされ、unstated のコンセプトをよりコン パクトに実現できるようになったため、Hooks ベースの API に⼀新して unstated-next という別パッケージで 2019年5⽉にリリースされた。
  9. まず、 Context と Hooks だけで状態管理を実装すると こんなコードになる

  10. useState() あるいは useReducer() を使い、 state とその更新 ロジックを内部に保持したカスタム Hook を⽤意する。

  11. このカスタムHook をコンポーネントで直接利⽤すると、Hook 内の state は コンポーネントごとのローカル state となるが、Context で管理することで、 同じ

    state の値を複数のコンポーネントで利⽤できるようになる。
  12. <Context.Provider> にカスタムHookの中⾝を渡す。 これにより、Contextの中⾝ = state が更新されると、 コンポーネントが変更を検知して、再描画するようになる。

  13. コンポーネントでは useContext() を使って、
 Context の中⾝ = state を取得し利⽤することができる。

  14. これだけで⼗分機能する。

  15. state logics こういうイメージ Context custom hook

  16. Context を Container(容器、⼊れ物)と呼ぶと、 よりイメージしやすくなる。 Container state logics custom hook

  17. さっきのコードを unstated-next を使って 書き換えてみると、こうなる。

  18. createContainer(customHook) で Container を作成し、 カスタムHookを格納する。

  19. Container は Context と同様、Provider を持つ。

  20. コンポーネントでは useContainer() を使って、
 Container の中⾝ = state を取得し利⽤することができる。

  21. これだけ。

  22. unstated-next を使わなくても⼗分シンプルだが、 unstated-next による「Container」という概念を使うことで よりわかりやすくなる。

  23. Redux よりも優れている点 • ファイルサイズは 1/40 • 学習コストが圧倒的に低い(Context と Hooks さえ知っていればいい)

    • ほぼ素の React なので、どんなライブラリとも連携しやすい • パフォーマンスの⾯でも有利
  24. Redux よりも優れている点 • ファイルサイズは 1/40 • 学習コストが圧倒的に低い(Context と Hooks さえ知っていればいい)

    • ほぼ素の React なので、どんなライブラリとも連携しやすい • パフォーマンスの⾯でも有利 ← どういうことか?
  25. Redux Store container component container component container component container component

    Redux では、State が更新されると、マウント中の すべての Container Component(Store とつながっているコンポーネント)が、変更の通知を受けとって、 mapStateToProps(ある いは useSelector) を実⾏する mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
  26. Redux Store container component container component container component container component

    処理の遅い mapState が1つ存在していると、state が変更されるたびに毎回実⾏されるため、 アプリケーション全体のパフォーマンスを落とす重⼤なボトルネックとなる。 mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
  27. Redux Store container component container component container component container component

    「⽬に⾒えて遅くはないが、微妙に遅い」という mapState でも、たくさん存在すると、 やはりパフォーマンス劣化の要因となってくる。 mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
  28. reselect によるメモ化など、 回避するためのベストプラクティスは存在するが、 そもそも、グローバルな単⼀の Store に すべてのコンポーネントが依存している という構造が引き起こす問題。

  29. component component component component component container container container container 次のように、Container

    (≒ Store) を複数に分けて、コンポーネントが本当に必要としている 場合だけ共有するようになっていれば、この問題は避けられる。
  30. component component component component component container container container container もしパフォーマンス上問題のあるコンポーネントが存在していても、⾃分に関係のない

    state が 変更されたときは何も起こらないので、コンポーネントが与える影響範囲は限定される。
  31. unstated-next では、
 Redux のようにアプリケーション全体の State を ⼀つの巨⼤な Container で管理するのではなく、 適切な粒度に分割して管理することが推奨されている。

    ただし、Container の粒度に関する制約やルールは
 存在していないので、⾃分でルールを決めて運⽤する必要がある。
  32. 気になる点や個⼈的な所感

  33. Container の最適な粒度は? • ⼩さすぎると管理が煩雑になりそうだし、⼤きすぎると Redux が抱える のと同じ問題を引き起こす危険性が増す。 • まずは、ある程度の機能ごとに分割してみて、少しづつ調整していく感じ になりそう。

  34. Container の最適な粒度は? • 正規化(normalize)されたデータは判断しやすい。
 例えば、製品データ を「byId」と「visibleIds」に正規化したとすると、 それぞれを Container にすれば、<ProductList> と

    <ProductDetail> で必 要とする State が良い感じに分離できる。
  35. Redux で Store を複数に分割すればいいのでは? • Redux の FAQ に回答が載っている。
 可能は可能だけど、Redux

    DevTools とか使えなくなるし、単⼀の Store で使ってもらうことを想定している、とのこと。
 
 https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple- stores-can-i-import-my-store-directly-and-use-it-in-components- myself
  36. 「Provider ⼊れ⼦地獄」にならないか? • 多くの Container を必要するコンポーネントでは <Container.Provider> の⼊れ⼦がかなり深くなる。
 


  37. 「Provider ⼊れ⼦地獄」にならないか? • helper 関数を⽤意するぐらいしか回避策はなさそう。
 https://github.com/jamiebuilds/unstated-next/issues/ 35#issuecomment-500255562 • ⼊れ⼦があまりにも深い場合は、Container の粒度が細かすぎるか、コン

    ポーネントの責務が⼤きすぎる可能性が考えられるので、
 リファクタリングのポイントといえるかも。
  38. 最適化のコツは? • Redux での「reselectを使いましょう」的な、unstated-next ならではの 最適化テクニックは特にない。 • 素の React とほぼ同じなので、React

    のスタンダードな最適化がそのまま 使える。
 https://github.com/jamiebuilds/unstated-next#tip-3-optimizing- components
  39. デバッグはどうする? • 専⽤のデバッグツールはない。 • React Developer Tools に Hooks のサポートが⼊っていて、最新の

    state の値は確認できるが、変更履歴などは確認できない。
 https://github.com/facebook/react-devtools/pull/1272 • これが⼀番つらいところかも。
  40. 今すぐ Redux を捨てて unstated-next に乗り換えるべきか? • Redux よりも簡単にコーディングできるのは間違いないが、Redux とは 別の全体設計⼒が試される。

    • 既存のアプリケーションに導⼊するのであれば、パフォーマンスチューニ ングの⼀環として、書き換えが頻発する State の⼀部を unstated-next に 置き換えてみるのはありだと思う。
  41. スケールするのか?(⼤規模アプリでも使えるか?) • Redux を使って安定した⼤規模アプリケーションを実装できる⼒量があれ ば、unstated-next を使ってもうまくいくのではないか。 • 個⼈的には、最適なアプリケーションのアーキテクチャを探っていきた い。

  42. スケールするのか?(⼤規模アプリでも使えるか?) • Container はクリーンアーキテクチャの Presenter(ユースケースやエンティティのデータ 形式を変換して、外界である UI に伝える役割)に あたるように思うが、React にがっつり依存して

    いるのでこのレイヤーなのか少しモヤモヤする。 • クリーンアーキテクチャに適⽤するなら unstated-next ではなく unstated のほうが
 ぴったりはまりそうな印象。
  43. もし実戦投⼊してノウハウが溜まったら、ぜひシェアしてください!

  44. おわり