Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
unstated-next による Redux に頼らない状態管理の考察
Yoshihide Jimbo
July 19, 2019
Programming
8
3.9k
unstated-next による Redux に頼らない状態管理の考察
React.kyoto v0.3.0 でのLT発表資料です。
https://react-kyoto.connpass.com/event/137847/
Yoshihide Jimbo
July 19, 2019
Tweet
Share
More Decks by Yoshihide Jimbo
See All by Yoshihide Jimbo
jmblog
29
20k
jmblog
21
5k
jmblog
3
630
jmblog
10
2.6k
jmblog
29
4.8k
jmblog
25
3.8k
jmblog
39
8.2k
jmblog
31
5.5k
jmblog
53
7.8k
Other Decks in Programming
See All in Programming
palkan
2
540
seike460
8
2.3k
daipresents
0
370
fr0gger
2
2.9k
drumato
1
260
borkdude
1
190
minodriven
39
29k
tkmnzm
1
260
timeseriesfr
1
150
d_endo
1
530
77web
2
2.6k
n1215
1
500
Featured
See All Featured
rasmusluckow
318
19k
geoffreycrofte
27
1.1k
roundedbygravity
84
7.9k
zenorocha
298
40k
dotmariusz
94
5.5k
pedronauck
652
110k
marktimemedia
7
460
jmmastey
11
710
bkeepers
PRO
54
4.3k
myddelton
109
11k
roundedbygravity
242
21k
destraynor
222
47k
Transcript
unstated-next による Redux に頼らない状態管理の考察 React.kyoto v0.3.0 | Jul 19, 2019
神保 嘉秀 @jmblog じんぼ よしひで
None
今⽇話すこと • unstated-next の紹介 • 気になる点や個⼈的な所感
unstated-next の紹介
https://github.com/jamiebuilds/unstated-next unstated-next
Hooks の登場によって、 Redux などの「状態管理ライブラリ」に頼らず、 React の Context と Hooks だけで状態管理を実装することが
可能になった。 unstated-next はその実装をサポートしてくれる、 必要最⼩限なライブラリ(わずか 200バイト)
unstated-next と unstated の関係 • unstated というライブラリもあってちょっとややこしい。 • 開発者はどちらも @jamiebuilds
⽒。 • unstated は Context を活⽤したシンプルな状態管理ライブラリ。 2018年前半に発表された。 • その後、React から Hooks がリリースされ、unstated のコンセプトをよりコン パクトに実現できるようになったため、Hooks ベースの API に⼀新して unstated-next という別パッケージで 2019年5⽉にリリースされた。
まず、 Context と Hooks だけで状態管理を実装すると こんなコードになる
useState() あるいは useReducer() を使い、 state とその更新 ロジックを内部に保持したカスタム Hook を⽤意する。
このカスタムHook をコンポーネントで直接利⽤すると、Hook 内の state は コンポーネントごとのローカル state となるが、Context で管理することで、 同じ
state の値を複数のコンポーネントで利⽤できるようになる。
<Context.Provider> にカスタムHookの中⾝を渡す。 これにより、Contextの中⾝ = state が更新されると、 コンポーネントが変更を検知して、再描画するようになる。
コンポーネントでは useContext() を使って、 Context の中⾝ = state を取得し利⽤することができる。
これだけで⼗分機能する。
state logics こういうイメージ Context custom hook
Context を Container(容器、⼊れ物)と呼ぶと、 よりイメージしやすくなる。 Container state logics custom hook
さっきのコードを unstated-next を使って 書き換えてみると、こうなる。
createContainer(customHook) で Container を作成し、 カスタムHookを格納する。
Container は Context と同様、Provider を持つ。
コンポーネントでは useContainer() を使って、 Container の中⾝ = state を取得し利⽤することができる。
これだけ。
unstated-next を使わなくても⼗分シンプルだが、 unstated-next による「Container」という概念を使うことで よりわかりやすくなる。
Redux よりも優れている点 • ファイルサイズは 1/40 • 学習コストが圧倒的に低い(Context と Hooks さえ知っていればいい)
• ほぼ素の React なので、どんなライブラリとも連携しやすい • パフォーマンスの⾯でも有利
Redux よりも優れている点 • ファイルサイズは 1/40 • 学習コストが圧倒的に低い(Context と Hooks さえ知っていればいい)
• ほぼ素の React なので、どんなライブラリとも連携しやすい • パフォーマンスの⾯でも有利 ← どういうことか?
Redux Store container component container component container component container component
Redux では、State が更新されると、マウント中の すべての Container Component(Store とつながっているコンポーネント)が、変更の通知を受けとって、 mapStateToProps(ある いは useSelector) を実⾏する mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
Redux Store container component container component container component container component
処理の遅い mapState が1つ存在していると、state が変更されるたびに毎回実⾏されるため、 アプリケーション全体のパフォーマンスを落とす重⼤なボトルネックとなる。 mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
Redux Store container component container component container component container component
「⽬に⾒えて遅くはないが、微妙に遅い」という mapState でも、たくさん存在すると、 やはりパフォーマンス劣化の要因となってくる。 mapStateToProps() mapStateToProps() mapStateToProps() mapStateToProps()
reselect によるメモ化など、 回避するためのベストプラクティスは存在するが、 そもそも、グローバルな単⼀の Store に すべてのコンポーネントが依存している という構造が引き起こす問題。
component component component component component container container container container 次のように、Container
(≒ Store) を複数に分けて、コンポーネントが本当に必要としている 場合だけ共有するようになっていれば、この問題は避けられる。
component component component component component container container container container もしパフォーマンス上問題のあるコンポーネントが存在していても、⾃分に関係のない
state が 変更されたときは何も起こらないので、コンポーネントが与える影響範囲は限定される。
unstated-next では、 Redux のようにアプリケーション全体の State を ⼀つの巨⼤な Container で管理するのではなく、 適切な粒度に分割して管理することが推奨されている。
ただし、Container の粒度に関する制約やルールは 存在していないので、⾃分でルールを決めて運⽤する必要がある。
気になる点や個⼈的な所感
Container の最適な粒度は? • ⼩さすぎると管理が煩雑になりそうだし、⼤きすぎると Redux が抱える のと同じ問題を引き起こす危険性が増す。 • まずは、ある程度の機能ごとに分割してみて、少しづつ調整していく感じ になりそう。
Container の最適な粒度は? • 正規化(normalize)されたデータは判断しやすい。 例えば、製品データ を「byId」と「visibleIds」に正規化したとすると、 それぞれを Container にすれば、<ProductList> と
<ProductDetail> で必 要とする State が良い感じに分離できる。
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
「Provider ⼊れ⼦地獄」にならないか? • 多くの Container を必要するコンポーネントでは <Container.Provider> の⼊れ⼦がかなり深くなる。
「Provider ⼊れ⼦地獄」にならないか? • helper 関数を⽤意するぐらいしか回避策はなさそう。 https://github.com/jamiebuilds/unstated-next/issues/ 35#issuecomment-500255562 • ⼊れ⼦があまりにも深い場合は、Container の粒度が細かすぎるか、コン
ポーネントの責務が⼤きすぎる可能性が考えられるので、 リファクタリングのポイントといえるかも。
最適化のコツは? • Redux での「reselectを使いましょう」的な、unstated-next ならではの 最適化テクニックは特にない。 • 素の React とほぼ同じなので、React
のスタンダードな最適化がそのまま 使える。 https://github.com/jamiebuilds/unstated-next#tip-3-optimizing- components
デバッグはどうする? • 専⽤のデバッグツールはない。 • React Developer Tools に Hooks のサポートが⼊っていて、最新の
state の値は確認できるが、変更履歴などは確認できない。 https://github.com/facebook/react-devtools/pull/1272 • これが⼀番つらいところかも。
今すぐ Redux を捨てて unstated-next に乗り換えるべきか? • Redux よりも簡単にコーディングできるのは間違いないが、Redux とは 別の全体設計⼒が試される。
• 既存のアプリケーションに導⼊するのであれば、パフォーマンスチューニ ングの⼀環として、書き換えが頻発する State の⼀部を unstated-next に 置き換えてみるのはありだと思う。
スケールするのか?(⼤規模アプリでも使えるか?) • Redux を使って安定した⼤規模アプリケーションを実装できる⼒量があれ ば、unstated-next を使ってもうまくいくのではないか。 • 個⼈的には、最適なアプリケーションのアーキテクチャを探っていきた い。
スケールするのか?(⼤規模アプリでも使えるか?) • Container はクリーンアーキテクチャの Presenter(ユースケースやエンティティのデータ 形式を変換して、外界である UI に伝える役割)に あたるように思うが、React にがっつり依存して
いるのでこのレイヤーなのか少しモヤモヤする。 • クリーンアーキテクチャに適⽤するなら unstated-next ではなく unstated のほうが ぴったりはまりそうな印象。
もし実戦投⼊してノウハウが溜まったら、ぜひシェアしてください!
おわり