カスタムフックを使用した共有状態

9758 ワード

多くの記事は、反応の反応状態管理戦略について書かれており、はい、これはまだそれらの別の1つです.しかし、新しい図書館とどのように誰もがそれを採用する必要があります話をするよりも、私は2つの異なる哲学的なアプローチについて話をしたい:中央集権対分散.

分権化国家パラダイム


分散状態は、反応の最初のバージョン以来、バッテリーが含まれて解決されています.クラスのコンポーネントは、再レンダリングをトリガするローカル状態を編集することができます機能コンポーネントは、useStateフックで同じことを達成することができます.
ローカル状態は、ディスプレイと副作用のためにそれを使用するコンポーネントの近くに座っているため、操作が非常に直感的です.

集中化した国家パラダイム


つのデータ構造(通常木)にあなたの反応するアプリケーションを提出するのに必要なすべての状態を含みます.そのツリーのブランチが状態を変更すると、新しい階層を再表示します.

全体のアプリケーションの状態のための参照の1つのポイントは非常にきちんとしたアイデアであり、反応自体の上に自然な進化のように感じている:コンポーネントを使用してアブストラクトDOMの変更、大きなJSON値とアプリの状態の概要の変更を変更します.
すべての変更がデータで表されるので、異なる状態を通してアプリケーションを遷移させるロジックは非常に簡単に表現し、テストすることができます.また、簡単にアプリの状態をモックで多くの有効なUI画面を生成することです.

speccardsはスペックに基づいてランダムに有効なUI状態を生成する

分散状態の問題


つのコンポーネントが同じ状態で見て、行動する必要があるとき、ローカル州は問題になります.単純な通貨コンバータは、ユーザーがいずれかの通貨のテキスト入力を編集することができますを考える.
この場合、Naive解決は最も近い親に「状態を上げる」ことであり、状態を操作するためにすべての子供のコールバック関数を与えます.

バニラ反応のLifting State Up
これはより大きなアプリケーションのためにめったにスケールしないので、より強力な共有のためにRecoilのような何かが必要かもしれません.

集中状態の問題


あなたは簡単にf(currentState, event) => newStateフックで集中した状態から始めて、彼らが必要であるところでuseReducerとして州を通過して、トップ<App/>構成要素の中にすべての州を保つことができます.あなたがすぐに十分に見つけるものは、しかし、ルートから遠く離れた構成要素が永久により多くの情報を必要とするということです、そして、彼らが正しい成分に達するまで、あなたは木を通してすべての小道具を訓練しなければなりません.
これは、再び、大きなアプリケーションのための規模ではありませんので、州のツリーの一部にpropsのいくつかの形式は、アプリケーションのパフォーマンスを維持し、拡張を容易にする必要があります.あなたはhook version of Reduxのような何かを必要とするかもしれない

開発者の経験の2つの比較


私は、常に集中化されたアプローチの巨大な支持者でした.それは機能的純度のために私に訴えました、そして、それが前フック時代で非常により簡単にアクセスして、操作する方法.私は以来、私の心を変更し、理由を説明したいと思います.
集中状態は、あなたに正しさの幻想を与えます.状態はきちんと1つの純粋な値からあなたのビジネスロジックをカプセル化する少しの機能に感謝します.以下のような関数をテストすることもできます.
- When button is clicked
- Set the loading indicator to true
- Clear the input field
- Send an http call to the server
を除いて、実際にアプリケーションを実行すると、動作はあなたが予想したものではありません.問題は、あなたのロジックがあなたのUIコンポーネントから遠ざかるということです、そして、原理的に音と正しさの間、それはあなたのレンダリングコードでよく遊びません.それは、状態が異なった形を持っているかもしれないが、よりしばしば、それは意図していない値が適切な時刻に状態に加えられないので、それです.
状態の遷移としてのアプリケーションのモデリングは、ほとんどの場合は、ゲームのような同期的な相互作用、ほとんどの取引がうまく動作しますが、インターネット上の新しいデータを取得するように、より非同期の動作でその限界を表示し始める.データがどのように変化するかを説明するのは簡単です、しかし、データが最初にそこに着く方法でない.ので、アクションがトリガされたり、適切なタイミングでユーザーが新しい画面に移動したときに受信されていないので、無限のスピナーで終わる可能性があります.

それはEhが起こるとき迷惑?

状態、効果とUIの共存


集中化された国家アプローチの支持者は大きな議論をします:UIは国家管理と副作用を気にしてはいけません.他のどこかのロジックを取り出してテストします.
原則的には、これは実際にはうまくいきません.逆は本当である傾向があります:それは、よく定義されたふるまいでスクリーンの断片として構成要素について考えるのに役に立ちます.アクションをトリガーし、イベントを聞き、他のコンポーネントに影響する状態を操作できます.
昨年、私は複雑な振る舞いをしているコンポーネントを書いていました.そして、完全に物事が壊れると予想して、私が若干の集中した解決で彼らをリファクタリングしているということを知っています.驚いたことに、この些細な、大規模なアプリケーションは、大きくなって、より複雑な取得せずに新しい機能を蓄積し続けた.私は、redux、clojurescriptまたはelmアプリが過去にされなかった方法でスケールしたとさえ言います、正当性のより少ない「正式な証明」(テスト、タイプ)でさえ.

名前のついたフック


このコンポーネント
function ItemList() {
 const { auth } = useUser()
 const { itemsPerPage } = usePreferences()
 const { searchQuery } = useSearch()
 const items = useItems({ auth, itemsPerPage, searchQuery })
 return <ul>{items.map(...)}</ul> 
}
私にとって、それは明確にその依存性(入力)とどのようにディスプレイのためにそれらを使用して通信します.また、これらの入力は静的データではなく、状態と効果の組み合わせである.
  • subscriptionsはログイン時に現在のユーザ情報を返し、そうでなければ認証フロー
  • をトリガする
  • useUserはIndexedDB
  • から非同期的にデータを保存します
  • usePreferences現在のURL
  • のクエリパラメータを抽出します
  • useSearch一緒にすべての関係、アイテムサーバー側を取得し、将来の使用のためにキャッシュ.
  • これらの依存関係のそれぞれが変更(URLパラメータ、またはローカルストレージがクリアされた場合、またはユーザートークンが期限切れになっている場合)フックは、コンポーネントの再取得および再描画の面倒を見るでしょう.
    キャッシュをカプセル化するカスタムフックは、コンポーネント間のデータを共有する素晴らしい方法です:ユーザ、設定、およびリモートデータはすべてメモリ内に存在し、一度だけフェッチする必要があります.フックを使用する最初のコンポーネントは、非同期の動作をトリガーし、他のすべてのコンポーネントは、その後無料でアクセスできます.
    この動作をフック自体で1つのステップに移動してラップすることもできます.
    function useSearchItems() {
     const { auth } = useUser()
     const { itemsPerPage } = usePreferences()
     const { searchQuery } = useSearch()
     const items = useItems({ auth, itemsPerPage, searchQuery })
     return items
    }
    
    だから、アプリケーションの2つの異なる部分の項目の同じリストを持つことができます:リスト自体とカウントインジケータどこか他の.
    フックは依存グラフとして見ることができます.あなたが反応フックで固執して、集中化された州管理でそれの外に行かないならば、このグラフを編集することは本当に簡単です.
    このパターンは、本当にコンポーネントを作成し、新しいコンポーネントを作成するスケール.別のコンポーネントは、このリストにアクセスする必要がありますか?それは1行の変更です.そのデータへのアクセスをもう必要としないでください?フックまたはコンポーネント自体を削除することは、そのスクリーンにもう取り込まれないことを意味します.
    状態(キャッシュされている)、動作(これは負荷を無効にし、データを無効にする)とUIは、密接に一緒にリンクすることができますので、彼らはあなたのアプリと一緒に有機的に進化する.

    そのためのアプリがあります


    ロード、キャッシュ、無効に時間がかかるの手動でフックを書いて、そこにそれを行う必要はありませんが、そこに素晴らしいライブラリが既にある.
    React Queryは、明らかにその驚くべきDXで目立ちます.HTTP、GraphSQL、および任意の非同期動作を実際に動作します.
    このライブラリだけでは、それはあなたが必要とする唯一のソリューションかもしれないので、多くの活用を提供します.我々が非同期の振舞いなしで状態共有について話しているならば、反則も良い追加であるかもしれません.あなたが記述的な名前でカスタムフックを使用しているならば、それは本当にあなたが下で使用するどの図書館でも重要でありません.
    // Prefer this
    const repos = useRepos()
    
    // To this
    const { data: repos } = useQuery('repoData', () => fetch('https://api.github.com/repos/tannerlinsley/react query').then(res => res.json()))
    

    実世界例


    私は私がfirebuzz.appを作成したので、アイデアが実際に実際に実装されている方法を示すために、それは常に最高です

    選手の質問に答えて、ポイントを受信バズすることができます
    ソースコード(およびそれは特定のuseItemsフォルダ)をhttps://github.com/frankiesardo/firebuzzで閲覧できます.