ファーストビューの効率化のために、ファーストビューのタイミングで不要なものを遅延ロードにしていたのですが、reducerを遅延させるのもいいかなと思えてきました。
reducerが必要となるタイミング
コンポーネントを描画した直後にdispatchを行う必要性というのは、そうそう生じないものです。ユーザーインタラクションでしかdispatchが行われない、ということでしたら、コンポーネントを作成したタイミングで遅延ロードを開始して、そこから1秒もしないうちにロードされる、というようなタイミングでじゅうぶん間に合います。
そして、巨大なオブジェクトを取り扱うreducerは、往々にして多くのコードを必要とします。
とはいえ、ふつうのReact.useReducerでは、reducer関数は同期的に必要となってしまいます。
useLazyReducerを作ってみた
ということで、遅延ロードでreducerをロードするようなHooksを立ててみました。
// ロード前は何もしないreducerを入れておく
const temporaryReducer = (store, _action) => store;
export default function useLazyReducer(lazyReducer, initialArg, initializer) {
// 遅延データを入れる
const reducerRef = React.useRef(temporaryReducer);
React.useEffect(() => {
lazyReducer().then((obj) => {
reducerRef.current = obj.default;
});
}, [lazyReducer]);
const reducer = React.useCallback(
(store, action) => reducerRef.current(store, action),
[]
);
return React.useReducer(reducer, initialArg, initializer);
}
仕掛けとしてはごく単純で、reducerとしてセットする関数はrefの中身につなぐだけ、としておいて、遅延ロードでrefを書き換える、というものです。lazyReducerとして渡すものは、React.lazyに倣って、「{default: reducer関数}をresolveするPromiseを返す関数」を受け取るようにしてあります。
使用感
遅延ロードが間に合わないぐらいのタイミングでユーザー操作に入るのは現実的に困難ですので、これで十分に間に合っている印象です。