$30 off During Our Annual Pro Sale. View Details »

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

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

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

Yoshihide Jimbo

July 19, 2019
Tweet

More Decks by Yoshihide Jimbo

Other Decks in Programming

Transcript

  1. unstated-next による

    Redux に頼らない状態管理の考察
    React.kyoto v0.3.0 | Jul 19, 2019

    View Slide

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

    View Slide

  3. View Slide

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

    View Slide

  5. unstated-next の紹介

    View Slide

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

    View Slide

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


    unstated-next はその実装をサポートしてくれる、

    必要最⼩限なライブラリ(わずか 200バイト)

    View Slide

  8. unstated-next と unstated の関係
    • unstated というライブラリもあってちょっとややこしい。
    • 開発者はどちらも @jamiebuilds ⽒。
    • unstated は Context を活⽤したシンプルな状態管理ライブラリ。

    2018年前半に発表された。
    • その後、React から Hooks がリリースされ、unstated のコンセプトをよりコン
    パクトに実現できるようになったため、Hooks ベースの API に⼀新して
    unstated-next という別パッケージで 2019年5⽉にリリースされた。

    View Slide

  9. まず、
    Context と Hooks だけで状態管理を実装すると
    こんなコードになる

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. コンポーネントでは useContext() を使って、

    Context の中⾝ = state を取得し利⽤することができる。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. コンポーネントでは useContainer() を使って、

    Container の中⾝ = state を取得し利⽤することができる。

    View Slide

  21. これだけ。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  27. Redux Store
    container component container component container component container component
    「⽬に⾒えて遅くはないが、微妙に遅い」という mapState でも、たくさん存在すると、
    やはりパフォーマンス劣化の要因となってくる。
    mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()

    View Slide

  28. reselect によるメモ化など、
    回避するためのベストプラクティスは存在するが、
    そもそも、グローバルな単⼀の Store に
    すべてのコンポーネントが依存している
    という構造が引き起こす問題。

    View Slide

  29. component component component component component
    container container container container
    次のように、Container (≒ Store) を複数に分けて、コンポーネントが本当に必要としている
    場合だけ共有するようになっていれば、この問題は避けられる。

    View Slide

  30. component component component component component
    container container container container
    もしパフォーマンス上問題のあるコンポーネントが存在していても、⾃分に関係のない state が
    変更されたときは何も起こらないので、コンポーネントが与える影響範囲は限定される。

    View Slide

  31. unstated-next では、

    Redux のようにアプリケーション全体の State を
    ⼀つの巨⼤な Container で管理するのではなく、
    適切な粒度に分割して管理することが推奨されている。
    ただし、Container の粒度に関する制約やルールは

    存在していないので、⾃分でルールを決めて運⽤する必要がある。

    View Slide

  32. 気になる点や個⼈的な所感

    View Slide

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

    View Slide

  34. Container の最適な粒度は?
    • 正規化(normalize)されたデータは判断しやすい。

    例えば、製品データ を「byId」と「visibleIds」に正規化したとすると、
    それぞれを Container にすれば、 と で必
    要とする State が良い感じに分離できる。

    View Slide

  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

    View Slide

  36. 「Provider ⼊れ⼦地獄」にならないか?
    • 多くの Container を必要するコンポーネントでは
    の⼊れ⼦がかなり深くなる。


    View Slide

  37. 「Provider ⼊れ⼦地獄」にならないか?
    • helper 関数を⽤意するぐらいしか回避策はなさそう。

    https://github.com/jamiebuilds/unstated-next/issues/
    35#issuecomment-500255562
    • ⼊れ⼦があまりにも深い場合は、Container の粒度が細かすぎるか、コン
    ポーネントの責務が⼤きすぎる可能性が考えられるので、

    リファクタリングのポイントといえるかも。

    View Slide

  38. 最適化のコツは?
    • Redux での「reselectを使いましょう」的な、unstated-next ならではの
    最適化テクニックは特にない。
    • 素の React とほぼ同じなので、React のスタンダードな最適化がそのまま
    使える。

    https://github.com/jamiebuilds/unstated-next#tip-3-optimizing-
    components

    View Slide

  39. デバッグはどうする?
    • 専⽤のデバッグツールはない。
    • React Developer Tools に Hooks のサポートが⼊っていて、最新の state
    の値は確認できるが、変更履歴などは確認できない。

    https://github.com/facebook/react-devtools/pull/1272
    • これが⼀番つらいところかも。

    View Slide

  40. 今すぐ Redux を捨てて unstated-next に乗り換えるべきか?
    • Redux よりも簡単にコーディングできるのは間違いないが、Redux とは
    別の全体設計⼒が試される。
    • 既存のアプリケーションに導⼊するのであれば、パフォーマンスチューニ
    ングの⼀環として、書き換えが頻発する State の⼀部を unstated-next に
    置き換えてみるのはありだと思う。

    View Slide

  41. スケールするのか?(⼤規模アプリでも使えるか?)
    • Redux を使って安定した⼤規模アプリケーションを実装できる⼒量があれ
    ば、unstated-next を使ってもうまくいくのではないか。
    • 個⼈的には、最適なアプリケーションのアーキテクチャを探っていきた
    い。

    View Slide

  42. スケールするのか?(⼤規模アプリでも使えるか?)
    • Container はクリーンアーキテクチャの
    Presenter(ユースケースやエンティティのデータ
    形式を変換して、外界である UI に伝える役割)に
    あたるように思うが、React にがっつり依存して
    いるのでこのレイヤーなのか少しモヤモヤする。
    • クリーンアーキテクチャに適⽤するなら
    unstated-next ではなく unstated のほうが

    ぴったりはまりそうな印象。

    View Slide

  43. もし実戦投⼊してノウハウが溜まったら、ぜひシェアしてください!

    View Slide

  44. おわり

    View Slide