useContextでコンテクストを使う」では、ロジックが含まれたカスタムコンテクストのプロバイダで、アプリケーションを包みました。具体的にでき上がったのは、前回のサンプル002です(以下に再掲)。今回はさらに、useReducerフックと組み合わせて、状態の操作と保持を切り分けます。コンテクストにuseReducerを使う前回は、アプリケーションのコンポーネントから、ロジックをコンテクストに切り出しました。このロジックの中身をさらに見ると、状態の操作と保持に分けられます。新たな状態をつくって保持するのがリデューサ(reducer)の役割です。useReducer()フックは、引数に受け取ったリデューサ関数(reducer)と状態の初期値(initialState)に対して、状態の参照(state)とアクション(action)配信関数(dispatch)が要素に収められた配列を返します。initialState);状態が変更されるべきことを伝えるのはアクションです。配信関数がリデューサに送ります。リスナーイベントとは違って、アクションごとのハンドラはありません。リデューサがまとめて扱うため、アクションは何が起こったのか示すtypeプロパティをもつことが必須です。リデューサはswitch文で、typeごとの処理を行うことになります。つぎのリデューサモジュールsrc/reducder.jsは、アクションのtypeをひとつだけ('increment')扱えるようにしたコードです。リデューサを用いた仕組みでは、引数から状態(state)は参照できるものの、状態を直には変えません。リデューサは新たなプロパティ値のオブジェクトを返すだけで、これにより状態が改められるのです。reducer;コンテクストのモジュールsrc/AppContext.jsは、useReducerフックの戻り値から受け取ったアクション配信関数(dispatch)により、アクションをリデューサに送ります。アクションにはtypeのほかにも、リデューサが必要とするプロパティを含めて構いません。複数のプロパティをひとつのオブジェクト(よくpayloadと名づけられます)にまとめてもよいでしょう。今回はtypeだけで足ります。useReducerは現在の状態も配列要素(state)に返しますので、必要な値(count)はそこから得てください。}}>{children}</AppContext.Provider>);};リデューサモジュールsrc/reducder.jsには、あとふたつ処理したいアクションのがあります。case文をつぎのようにふたつ加えましょう。コンテクストモジュールsrc/AppContext.jsの記述は、以下のコード001にまとめました。併せて、リデューサのコード全体も掲げます。それぞれのモジュールの具体的なコードと実際の動きは、CodeSandboxに公開したサンプル001でお確かめください。リファレンス」「useReducer」からの引用です。通常、useReducerがuseStateより好ましいのは、複数の値にまたがる複雑なstateロジックがある場合や、前のstateに基づいて次のstateを決める必要がある場合です。useReducerを使えばコールバックの代わりにdispatchを下位コンポーネントに渡せるようになるため、複数階層にまたがって更新を発生させるようなコンポーネントではパフォーマンスの最適化にもなります。アクションの配信にuseCallback()フックを使う前掲コード001でカウンターの動きはでき上がりです。つぎに、無駄を減らすことについて考えましょう。コンポーネント内に定めた関数は、放っておけばレンダーのたびにつくり直されます。それを必要なときだけ行うのが、useCallbackフックです。第1引数に呼び出す関数(コールバック)を渡します。大切なのば第2引数です。配列要素として渡した参照の値(依存値)が変わったときに関数はつくり直されます。[...依存値]);改めて、コンテクストモジュールsrc/AppContext.jsに定められた関数を見てみましょう。配信関数dispatchに引数として渡すアクションは、typeプロパティだけで値は変わりません。このようなとき第2引数の依存配列は空[]で与えればよいのです。第2引数を省くとレンダーのたびにコールバックはつくり直されるので、意味がなくなることにご注意ください。したがって、dispatchを呼び出す関数はすべてuseCallbackで包み、第2引数には空の依存配列[]を与えるということです。[]);};モジュールsrc/AppContext.jsの中身はつぎのコード002のように書き替えました。以下のサンプル002をCodeSandboxに公開しています。