概要
クロージャ(Closure) とは、
「関数が定義されたスコープの外部変数を保持し続ける仕組み」です。
Reactでは、コンポーネントが再レンダーされても関数の内部状態を保つ必要がある場面が多く、
この 「スコープの保持」=クロージャ が、Hooksや関数分離設計の基盤になっています。
つまり、
- ロジックを関数として分離し、スコープ内の値を保持できる
- React Hooks(
useEffect,useCallback,useMemo)などもクロージャを前提に動作 - 関数の再生成や副作用の管理にも直接関係する
目次
基本構文
まずは、関数が外部スコープを「覚える」例です。
function outer() {
let message = "Hello Closure";
function inner() {
console.log(message); // 外側のスコープを参照
}
return inner;
}
const fn = outer();
fn(); // "Hello Closure"
解説:
-
inner関数はouterの中で定義されているため、outerが終了してもmessageを保持しています。 - この「スコープを覚え続ける関数」が クロージャ(Closure) です。
Reactでのロジック分離
Reactコンポーネントでは、UI層とロジック層を分離することで再利用性と保守性を高められます。
このとき、外部スコープを保持できる関数(=クロージャ) を利用することで、
関数内部で状態や設定値を「記憶」できます。
1. 一部の引数を記憶するクロージャ(設定値のみ保持)
税率を記憶して金額を後から渡す関数
export function createTaxCalculator(taxRate) {
return function (price) {
return price * (1 + taxRate);
};
}
ここで
taxRateは、createTaxCalculatorが呼ばれた瞬間にクロージャとして記憶されます。
以後、priceを呼び出し時に渡すだけで税計算が実行されます。
Reactコンポーネントでの利用例
import React from "react";
import { createTaxCalculator } from "../utils/taxCalculator";
export default function TaxCalculator() {
const japanTax = createTaxCalculator(0.1); // 消費税10%
const usTax = createTaxCalculator(0.07); // 消費税7%
return (
<div>
<h3>Tax Calculator</h3>
<p>Japan: {japanTax(100)}</p>
<p>US: {usTax(100)}</p>
</div>
);
}
ポイント:
| 引数名 | 定義された関数 | 記憶されるか | タイミング |
|---|---|---|---|
taxRate |
外側関数 createTaxCalculator
|
✅ 記憶される | 関数生成時 |
price |
内側関数(戻り値の無名関数) | ❌ 呼び出し時に渡す | 関数呼び出し時 |
まとめ:
taxRate はスコープに閉じ込められ、price は毎回渡す値。
これにより、「設定値」と「入力値」が分離でき、ロジックを再利用しやすくなります。
2. すべての引数を記憶するクロージャ(完全固定化パターン)
💡 固定価格・固定税率を両方記憶する関数
export function createFixedPriceCalculator(taxRate, price) {
return function () {
return price * (1 + taxRate);
};
}
この関数では、
taxRateとpriceの両方を外側スコープで受け取り、
内側関数には引数を渡さずとも結果を返せる構造になっています。
Reactコンポーネントでの利用例
import React from "react";
import { createFixedPriceCalculator } from "../utils/fixedTaxCalculator";
export default function FixedTaxExample() {
const fixedCalc = createFixedPriceCalculator(0.1, 100);
return (
<div>
<h3>Fixed Tax Calculator</h3>
<p>Result: {fixedCalc()}</p>
</div>
);
}
解説:
| 引数名 | 定義された関数 | 記憶されるか | 役割 |
|---|---|---|---|
taxRate |
外側関数 | ✅ 記憶される | 設定値(定数的) |
price |
外側関数 | ✅ 記憶される | 一度決まった価格 |
| なし | 内側関数 | ❌ 引数不要 | 呼び出すだけで結果を得る |
ポイント:
- 両方を外側関数で受け取ることで、「完全に固定化された関数」を作れる。
- たとえば「特定条件でしか使わない割引計算」など、ロジックを使い回す際に有効。
- ただしクロージャの保持範囲を広げすぎると、メモリ効率が悪くなる点に注意。
まとめ:引数の記憶ルール
| 区分 | 定義場所 | 記憶されるか | 呼び出し時に必要か | 典型的な用途 |
|---|---|---|---|---|
| 外側関数の引数 | 外部スコープ | ✅ 記憶される | ❌ 必要なし | 設定値・環境値を保持 |
| 内側関数の引数 | 実行スコープ | ❌ 記憶されない | ✅ 都度渡す | 実行時データを処理 |
useCallbackとクロージャ
Reactの useCallback も、クロージャの仕組みを前提に設計されています。
useCallback は「特定のスコープを保持した関数を再生成しない」ためのHookです。
例:通常のクロージャとuseCallbackの違い
import React, { useState, useCallback } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
// 通常の関数 → 毎回新しいクロージャが生成される
const handleIncrement = () => {
setCount(count + 1);
};
// useCallback → countが変化した時だけ新しいクロージャを生成
const memoizedIncrement = useCallback(() => {
setCount(c => c + 1); // 最新の状態を安全に参照
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>+1 (再生成あり)</button>
<button onClick={memoizedIncrement}>+1 (useCallback)</button>
</div>
);
}
解説:
| 項目 | 通常関数 | useCallback |
|---|---|---|
| クロージャの再生成 | 毎回生成される | 依存配列変更時のみ生成 |
| 状態の参照 | 呼び出し時点の値を保持(古い可能性あり) | 依存関係で管理 |
| Reactでの効果 | 再レンダーごとに新しい関数 | メモ化により安定した関数参照 |
ポイント:
-
useCallbackは 「再生成されないクロージャ」 を作るHook。 - コンポーネントの再レンダー時も、同じ関数オブジェクトを再利用できる。
- 関数が子コンポーネントに渡される場合の再レンダー最適化に有効。
まとめ
| シーン | クロージャの目的 | Reactでの実装例 |
|---|---|---|
| 設定値の保持 | 外部スコープを覚える | createTaxCalculator |
| 状態を隠す | 変数をプライベート化 | createCounter |
| 関数の安定化 | 再生成を防止 | useCallback |
| 副作用の制御 | スコープ内の値を参照 | useEffect |
Reactの関数コンポーネントは、クロージャの性質を理解してこそ安全に設計できる。
特にuseCallback・useEffectは「関数がどのスコープを参照しているか」を明確に意識することが重要です。