【ITニュース解説】Factory Functions in useMemo()
2025年09月19日に「Dev.to」が公開したITニュース「Factory Functions in useMemo()」について初心者にもわかりやすく解説しています。
ITニュース概要
ファクトリ関数は、オブジェクトや関数を生成する関数で、`new`キーワードは不要。Reactの`useMemo`は、このファクトリ関数を受け取り、依存関係が変化した時のみ実行し値を生成する。これにより、無駄な再計算を防ぎ、パフォーマンスを改善する。
ITニュース解説
ファクトリー関数とは、何か特定の処理を直接実行して結果を返すのではなく、新しいオブジェクトや関数、あるいはその他のリソースといった「もの」を生成して返す関数のことである。これは、まるで工場が製品を作るように、呼び出し元に特定の「もの」を提供する役割を果たす。
JavaScriptにおいて、ファクトリー関数はオブジェクト作成の一般的なパターンとして利用される。例えば、ユーザーを表すオブジェクトを複数作りたい場合を考えてみよう。毎回同じような構造のオブジェクトを手動で記述するのは効率が悪い。そこで、createUser(name)のようなファクトリー関数を定義することで、引数に名前を渡すだけで、その名前を持つユーザーオブジェクト(名前と挨拶するメソッドを持つ)を簡単に生成できる。このcreateUser関数が、まさにファクトリー関数である。
ファクトリー関数を使うことにはいくつかのメリットがある。一つは、JavaScriptでオブジェクトを生成する際に使うnewキーワードを避けることができる点だ。newキーワードはコンストラクタ関数と組み合わせて使われる特別な構文だが、ファクトリー関数は通常の関数なので、newを必要としない。これにより、コードの記述がシンプルになる場合がある。二つ目は、生成するオブジェクトをインスタンスごとに細かくカスタマイズしやすい点だ。ファクトリー関数は引数を受け取れるため、その引数の内容に応じて、返すオブジェクトの構造や振る舞いを柔軟に変更できる。三つ目は、JavaScriptのクロージャという強力な機能と相性が良い点だ。ファクトリー関数内で定義された変数を、その関数が返すオブジェクトや関数に閉じ込めることで、外部から直接アクセスできない「プライベートな状態」を作り出すことができ、データ隠蔽やモジュール化に役立つ。
Reactのフックの一つであるuseMemoにおいても、ファクトリー関数は重要な役割を果たす。useMemoは、計算コストが高い処理の結果をメモ化(キャッシュ)し、コンポーネントが再レンダリングされる際に、特定の依存関係の値が変わらない限り、前回の計算結果を再利用することでパフォーマンスを最適化する機能である。
useMemoは、useMemo(() => computeSomething(), [deps])のように利用される。この中の() => computeSomething()の部分が、useMemoに渡されるファクトリー関数である。開発者はこのファクトリー関数を直接実行するのではなく、Reactに「この計算を行うためのレシピ」として渡す。Reactは、[deps]で指定された依存配列内の値が前回のレンダリング時と比べて変化した場合にのみ、このファクトリー関数を呼び出し、新しい計算結果を生成する。依存配列の値に変化がなければ、Reactは以前に計算して保存しておいた値をそのまま再利用するため、無駄な再計算を避けることができる。
useMemoに渡すファクトリー関数は、引数を取らない形式でなければならない。Reactがこのファクトリー関数を呼び出す際、引数を一切渡さないためだ。もしファクトリー関数内で外部の変数を使いたい場合は、その変数を関数のスコープ外から「キャプチャ」する、つまりクロージャとして利用する必要がある。例えば、const value = useMemo(() => expensiveCalculation(input), [input]);のように、expensiveCalculation内でinputという変数を利用し、そのinputを依存配列にも含めることで、inputの値が変更されたときだけexpensiveCalculationが再実行されるようにする。
通常の関数とファクトリー関数には明確な違いがある。通常の関数は、呼び出されるとすぐに指定された処理を実行し、その結果を返す。例えば、add(2, 3)と呼び出せば、すぐに5という結果が得られる。これは「今すぐ処理を実行して結果を返せ」という命令に近い。
一方、ファクトリー関数は、呼び出し時にすぐには特定の処理を実行せず、新しい「もの」(オブジェクト、関数、リソースなど)を作り出して返す。createUser("Cathy")と呼び出せば、すぐにCathyという名前を持つユーザーオブジェクトが生成されて返される。これは「何かを作って返せ」という命令に近い。useMemoの文脈では、このファクトリー関数は「値を生成するためのレシピ」としてReactに提供され、Reactが必要と判断したときにそのレシピに従って値が生成される。
コンストラクタ関数との違いも見ておこう。コンストラクタ関数はnewキーワードと一緒に使われる特別な関数で、thisキーワードを使って新しいオブジェクトのプロパティを初期化する。これに対し、ファクトリー関数はnewキーワードを必要とせず、通常の関数として新しいオブジェクトや関数を明示的にreturnで返す点が異なる。
useMemoの文脈でファクトリー関数がいかに重要かを理解するために、具体的なコードの例を比較してみよう。
Reactコンポーネント内で、非常に計算コストの高い処理(例:数値演算を伴う重いループ)をuseMemoを使わずに直接呼び出す場合、コンポーネントが何らかの理由で再レンダリングされるたびに、その高コストな処理が毎回実行されてしまう。たとえその計算の入力値が変わっていなくても再計算が行われるため、CPUリソースの無駄遣いとなり、アプリケーションのパフォーマンス低下を招く。
しかし、useMemoにファクトリー関数と依存配列を渡すことで、この問題を解決できる。
useMemo(() => expensiveCalculation(n), [n]);のように記述すると、() => expensiveCalculation(n)がファクトリー関数となる。Reactは初回のレンダリング時にこのファクトリー関数を実行し、その結果をキャッシュする。以降のレンダリング時には、Reactは依存配列[n]の中のnの値が前回のレンダリングから変更されたかどうかをチェックする。nの値が同じであれば、Reactはファクトリー関数を再実行せず、キャッシュしておいた結果を再利用する。nの値が変更された場合にのみ、ファクトリー関数が再度呼び出され、新しい計算が実行される。
この仕組みにより、計算コストの高い処理が本当に必要なときだけ実行されるようになり、無駄な再計算を劇的に削減できる。これがuseMemoがファクトリー関数を要求する理由であり、Reactアプリケーションのパフォーマンス最適化において中心的な役割を果たす。つまり、通常の関数が「今すぐ実行して結果をくれ」という指示であるのに対し、useMemoに渡すファクトリー関数は「このレシピを使って、必要になったら値を生成してくれ」という指示であり、この違いがReactアプリケーションの効率的な動作に直結しているのだ。