なぜ私は常にコンテキストをラップします.プロバイダとUSEtext


反応コンテキストはクールな機能です、そして、私は構成を注射して、コンテナ/子供コンポーネントAPIを作るためにそれを大いに使います<RadioGroup /> + <RadioButton /> ). 残念ながら、ボックスコンテキストの制限と非常に便利なAPIが付属しています.ほとんどの場合、カスタムコンポーネントとフックを使用してプロバイダーとコンシューマーをラップします.私が強調する問題のいくつかは、ライブラリ管理者により関連していますが、ほとんどはアプリ開発にも適用されます.
このポストでは、我々はAdaptivityContext これにより、コンポーネントはビューポートの次元データを読み込むことができますwidth ブレークポイントのステータスisMobile :
const getWidth = () => window.innerWidth;
const isMobile = (w: number) => w < 600;
const AdaptivityContext = createContext({
  w: getWidth(),
  isMobile: isMobile(getWidth),
});
あなたが読んだならばpost on Context performance issues, あなたはそれが最良のデザインの選択ではない-知っているだけの成分isMobile まだすべての上で再レンダリングされますwidth チェンジ.それでも、それが我々が我々のプロジェクトに持っているものであると仮定してください.カスタムはどうAdaptivityProvider and useAdaptivity ヘルプ?


テキストをラップする
生の文脈APIでは、消費するコンポーネントは利用しますuseContext フックするContext.Consumer しかし、今日はフックの上で誰がなぜそれを選ぶかはわかりません.特に悪いことはないuseContext , しかし、我々はより多くのカスタムを行うことができますuseAdaptivity !
If useContext 使用外Provider , 静的なデフォルト値はcreateContext またはcryptic NULLのプロパティの幅を読み取ることができません.時々、それは十分ですAdaptivityContext ダイナミックであると思われます、そして、我々は「あなたがプロバイダーを忘れたか」と固定される「バグ報告」の多くを得ます.習慣useAdaptivity 2つのより強いオプションを与えます.
  • 明示的なエラーメッセージを表示するconsole.error('useAdaptivity must be used inside AdaptivityProvider')
  • 各コンポーネントを独立したサイズオブザーバAdaptivityProvider 高度な最適化とオーバーライドを選択できます.
  • 次はuseContext 文脈に1 : 1の関係があります.固定AdaptivityContext パフォーマンス問題は2つの別々の文脈にそれを分割することを含みますwidth , そして、より安定したものisMobile . useAdaptivity 両方のコンテキストを購読することができます-それは任意のパフォーマンスの利点を持っていないが、それは後方互換性があり、ユーザーが徐々に新しいAPIにアプリケーションを更新することができます:
    const useAdaptivity = () => {
      console.warn('Please migrate to useMobile or useViewport for better performance');
      const viewport = useContext(ViewportContext);
      const mobile = useContext(MobileContext);
      return { ...viewport, ...mobile };
    };
    
    カスタムuseAdaptivity フックも交互の文脈注入メカニズムを考慮に入れますreact-tracked . コンテキストの代わりにグローバル状態マネージャにバインドすることもできます.何もuseAdaptivity コンテキストとは何か関係があることを含意します.
    だから、習慣useAdaptivity フックは私たちに多くの自由を与えます-我々が望むように文脈を修正することができて、他の州管理メカニズムと彼らを交換して、我々が合うように、我々は不足しているプロバイダーを扱うことができます.それは納得がいく.アバウトProvider ?

    ラップコンテキスト.プロバイダもReact.createContext あなたを与えるContext.Provider コンテキスト値を渡すために使用するコンポーネントです.それはいくつかの重要な機能を欠いているが、我々は簡単にカスタムにそれを包むことによって修正することができますProvider コンポーネント.率直に言って、それは懸念より少ないuseContext — あなたはよくシングルを持っていますProvider , そして、それはいくつかのコンポーネントに位置する必要がありますので、あまりにも間違って行くことはできません.完全性のために、私が通常カスタムで行うものは、ここにありますProvider .
    Context.Provider オブジェクトコンテキストを使用すると、パフォーマンスの危険性がありますvalue 参照してください、すべてのコンテキスト消費者は、すべての再レンダリングされますProvider レンダリングは、厳密な平等の下でコンテキストの値が変更されるたびに、それらを更新します.私はなぜこの機能が反応中心でないかについて、わかりません、しかし、それはカスタムプロバイダーを持つ1つの良い理由ですpost on custom memo 詳細はuseObjectMemo ):
    const AdaptivityProvider = ({ children, ...context }) => {
      const contextValue = useObjectMemo(context);
      return (
        <AdaptivityContext.Provider value={contextValue}>
         {children}
        </AdaptivityContext.Provider>
      );
    };
    
    まさにuseContext , 生Providers コンテキストを持つ1 : 1の関係を持って、それを分割する/コンテキストをマージするのは難しい.結合を固定するwidth and isMobile 更新、我々は分割する必要がありますAdaptivityContext つの部分に.カスタムプロバイダを使用すると簡単です.
    const AdaptivityProvider = ({ children, width, isMobile }) => {
      const viewportValue = useObjectMemo({ width });
      const mobileValue = useObjectMemo({ isMobile });
      return (
        <ViewportSizeContext.Provider value={viewportValue}>
          <MobileContext.Provider value={mobileValue}>
            {children}
          </MobileContext.Provider>
        </ViewportSizeContext.Provider>
      );
    };
    
    まさにuseAdaptivity , AdaptivityProvider また、他の状態管理技術とコンテキストを置き換えることができます<StoreProvider> そこで、そして、あなたはされます.
    最後に、カスタムプロバイダーは、コンテキストの値をよりスマートな方法で処理することができます.デフォルトオプションを追加するか、ツリー上の別のプロバイダーとマージします.両方ともwidth and height , 私たちは部分的な上書きを許すことができました<ViewportSizeProvider width={100}> 狭いサイドバーで、保存中height
    const parentViewport = useContext(ViewportSizeContext);
    const contextValue = useObjectMemo({
      ...parentWiewport,
      ...size
    });
    
    もちろん、コンテキスト値の自動検出と更新のカスタム機構を持つこともできます.
    useLayoutEffect(() => {
      const cb = () => {
        setDetectedSize(getViewportSize());
      };
      window.addEventListener('resize', cb);
      return () => window.removeEventListener(cb);
    }, []);
    const contextValue = useObjectMemo({
      ...detectedSize,
      ...props
    });
    
    あなたは継承、自動検出と上書きの驚くべき組み合わせを持つことができます.実際には、無限の可能性がある場合は、コンテキストプロバイダのマスターです.ちょうど生で解決しないでくださいContext.Provider .
    カスタムフックにコンテキストのプロバイダーと消費者をラップすると、多くの柔軟性が得られます.
  • マージし、必要に応じてコンテキストを分割します.
  • 生のコンテキストを別のステートインジェクションテクニックに置き換えます.
  • コンテキストオブジェクトの値を安定化します.
  • コンテキスト値のスマートなダイナミックなデフォルトを導入します.
  • 他のプロバイダーから部分的にオーバーライドされたツリーを継承します.
  • 警告または不足しているプロバイダにフォールバック.
  • あなたがライブラリを構築している場合、この柔軟性は重要ですが、それはまた、任意の非自明なアプリで多くのことができます.あなたを説得する希望!またあとで.