こんにちは!Reactの学習、楽しく進めていますか?
画面がスイスイ動くようになってくると、「よし、どんどん作るぞ!」とテンションが上がりますよね。
でも、しばらくアプリを作っていると、なんだか「画面を開く瞬間だけ、妙にカクッとする…」とか、「ボタンを押したあとの動きが、ほんの少し重い気がする…」といった、得体の知れないモヤモヤにぶつかることがあります。
その原因、もしかすると`useState`の「初期値の渡し方」にあるかもしれません。
今回は、React初学者が「おっ、これ知っておくとちょっとドヤ顔できるかも!」という、遅延初期化(Lazy Initialization)というテクニックについて、身近な例えを交えながら優しく紐解いていきたいと思います。大丈夫、一歩ずつ見ていけば絶対に理解できますよ!
—
1. その「重い初期値」、毎回のレンダリングで計算してませんか?
まずは、私たちが普段よく使う`useState`の基本のおさらいから。
// カウンターの例
const [count, setCount] = useState(0);
この「0」という数字を渡す代わりに、「ものすごく時間がかかる複雑な計算結果」を初期値として入れたい場面を想像してみてください。例えば、大量のデータをこねくり回して分析する処理や、ローカルストレージをごそごそと漁る処理などです。
ここで、うっかりやってしまいがちなのが、こんなコードです。
// ❌ やりがちなパターン:毎回コストの高い計算が走っちゃう!
const [data, setData] = useState(重い計算をする関数());
「えっ、画面が最初に表示されるときだけ計算してくれればいいんじゃないの?」と思いますよね。実はここに、Reactのちょっとした「おせっかい(仕組み上の仕様)」が隠されています。
身近な例え:お店の「開店準備」で考えてみよう
これをカフェの開店準備に例えてみましょう。
- 理想の状態(遅延初期化):
シャッターを開けるその日の朝一番に、1回だけドリップコーヒーの仕込み(重い計算)をする。あとは、お客さんが来るたびにそのコーヒーを注ぐだけ。
- 間違った状態(通常の関数呼び出し):
お客さんが新しい席に座るたびに、「いらっしゃいませ!」の度に豆を挽き直して、一からコーヒーを淹れ直している。
後者のやり方だと、お客さんが増えれば増えるほど(つまり、画面が再レンダリングされるたびに)、裏で無駄なコーヒー豆と時間が消費されてしまいますよね。Reactも全く同じで、たとえ初回の表示が終わったあとであっても、画面が更新されるたびに`useState`のカッコの中にある「重い計算」を毎回こっそり実行してしまっているのです。
これではアプリの動きが重くなって当然ですよね。
—
2. 救世主!「関数」を渡すだけのラクラク解決法
そこで登場するのが、今回の主役である「遅延初期化(Lazy Initialization)」です。
やり方はとってもシンプル。`useState`の中に、計算結果そのものをドンと渡すのではなく、「計算をするための関数そのもの(アロー関数など)」をポロッと渡してあげるだけです。
// ⭕ 正しいパターン:初回のみ実行される!
const [data, setData] = useState(() => {
// ここに重い処理を書く
return 激重な計算結果();
});
「あれ? カッコの中に、また関数が入ってる……難しそう……」と思いました?
大丈夫です、怖がらなくていいですよ!
ここにアロー関数(`() => { … }`)を挟むことで、Reactに対してこう伝えていることになります。
> 「ねえReactさん、この重い計算の仕方は教えてあげるけど、今すぐ実行しなくていいからね。本当に最初の1回目、必要になったときだけ自分で呼び出してよ!」
Reactは賢いので、この「関数で包む」というサインを受け取ると、2回目以降の再レンダリングの際にはその計算を丸ごとスキップしてくれるようになります。これが、遅延初期化の正体です。
—
3. 【実践】動くコードで体感してみよう!
百聞は一見にしかず。実際にエディタに貼り付けて試せるサンプルコードを見てみましょう。
今回は、「画面を開いたときに、ちょっと重そうな計算をするカウンター」を作ってみます。
import React, { useState } from ‘react’;
// わざと処理を重く見せるための、時間がかかる関数(モック)
const getInitialHeavyCount = () => {
console.log(‘🔥【重要】重い初期化の計算が走りました!(これは初回のみ動くべき)’);
// 擬似的に重い処理(0.5秒かかるイメージ)
let sum = 0;
for (let i = 0; i < 100000000; i++) {
sum += i;
}
return 100; // 初期値として「100」を返す
};
export default function App() {
const [count, setCount] = useState(() => getInitialHeavyCount());
const [text, setText] = useState(”);
return (
useStateの遅延初期化 体験ルーム
現在のカウント: {count}
{/ カウントを増やすボタン /}
{/ 画面を再レンダリングさせるための文字入力欄 /}
文字を入力すると、画面が再レンダリングされます。
setText(e.target.value)}
placeholder=”ここになにか入力してね”
/>
);
}
試してみるポイントと挙動のチェック
1. このコードを動かして、ブラウザの開発者ツール(Console)を開いてみてください。
2. ページを開いた瞬間、コンソールに `🔥【重要】重い初期化の計算が走りました!` と表示されますよね。これは大正解です。
3. 次に、下のテキスト入力欄に文字をたくさん入力してみてください。文字を打つたびに画面(コンポーネント)は何度も再レンダリングされます。
4. ここでコンソールをよく見てください。追加のログが出ていませんよね?
もし、初めに `useState(getInitialHeavyCount())` と書いてしまっていたら(カッコの中に直接関数呼び出しを入れていたら)、テキスト欄に文字を1文字打つたびに、あの重いループ処理が毎回裏で実行されてしまい、アプリがカクついてしまいます。
関数で包む(`useState(() => getInitialHeavyCount())`)というひと手間を加えるだけで、Reactは「おっ、初回だけでいいんだな」と理解し、無駄な計算をピタッと止めてくれるんです。
—
4. どんなときに使うべき? 現場のリアルな判断基準
「じゃあ、これからはすべての`useState`で関数を使わなきゃいけないの?」というと、答えはNOです。
- 使わなくていいケース:
`useState(0)` や `useState(‘こんにちは’)` のような、単純な数値や文字列、真偽値(boolean)のとき。これらは計算コストがゼロに近いので、そのまま値を入れて何の問題もありません。わざわざ関数で包むとかえってコードが読みにくくなります。
- 絶対に使うべきケース:
- ローカルストレージからデータをゴソゴソと読み出す処理(`localStorage.getItem(…)` は意外と重いです)
- 複雑な配列やオブジェクトを作り出すための重たい計算処理
- 外部のAPIやライブラリを使って、初期値を同期的に生成するような処理
現場のシニアエンジニアたちも、「パフォーマンスチューニングの基本のキ」として、この遅延初期化を自然に使い分けています。
—
まとめ
今回は、`useState`の遅延初期化についてお話ししました。
- `useState`の初期値に直接重い関数を入れると、画面が再レンダリングされるたびに毎回実行されてしまう。
- アロー関数で包んで(`useState(() => …)`)渡してあげると、最初の1回目(初回レンダリング時)だけ実行されるようになる!
- 文字列や数字などの軽い値には不要だけど、重い処理やストレージ読み込みのときはこの「おまじない」を思い出そう。
「なんだかアプリの動きがもたつくな…」と感じたときは、ぜひコンポーネントの初期値まわりを見直してみてくださいね。あなたの書くReactコードが、より軽快でスマートなものに生まれ変わるはずです。
それでは、また次回のReactお悩み相談室でお会いしましょう!Happy Coding!

コメント