こんにちは!Reactの学習、楽しく進められていますか?
画面に文字が出たり、ボタンを押すと色が変わったりすると、「おっ、動いた!」という感動がありますよね。
でも、しばらく触っていると、なんだか不思議な現象に出会いませんか?
「あれ? 状態を更新したはずなのに、その直後の変数の値が変わっていない……?」
「ボタンを連打したら、画面の数字が変な増え方をした……!」
実はこれ、Reactを学び始めた人が全員通る、最初の「大きな壁」なんです。
でも、大丈夫ですよ。あなたがJavaScriptの書き方を間違えているわけでも、パソコンの不具合でもありません。これはReactという優秀な相棒が、「私たちのアプリをサクサク快適に動かすために、裏で頑張ってくれている証拠」なんです。
今日は、この状態更新の「非同期(ひどうき)」と、React 18からやってきた「バッチ処理」という魔法の仕組みについて、身近な例えを交えながら優しく解きほぐしていきましょう!
—
1. なぜすぐ変わらないの?お寿司屋さんの「注文メモ」で例えてみよう
まずは、`useState`を使ったときの私たちのイメージと、実際のReactの裏側のギャップを埋めていきましょう。
初心者の頃は、こう思いがちです。
> 「`setA(10)`って書いたんだから、その次の行ではもう `a` は `10` になっているはず!」
でも、残念ながらそうはなりません。これを例えるなら、「お寿司屋さんでの注文」に似ています。
あなたがカウンターに座って、職人さんに「マグロを1皿ください!」と声をかけたとします。
この「声をかけた瞬間」が、コードでいう `setCount(count + 1)` です。
ここで、声をかけた瞬間に、目の前にシャリとネタがピュッと現れるでしょうか?
いいえ、違いますよね。
1. あなたが注文する
2. 職人さんがメモを取る
3. 他の人の注文と一緒に少し待つ(タイミングを計る)
4. まとめて握って、あなたの目の前に「どうぞ」と出す
Reactの状態更新も、まったくこれと同じなんです。
`setCount` を呼んだ時点では、Reactは「ふむふむ、カウントを1増やしたいんだな」とメモ(予約)を受け取るだけ。その場ですぐに画面を書き換えたり、変数の中身を今すぐ書き換えたりはしないのです。これが、状態更新が「非同期(すぐには完了しない)」と言われる理由です。
—
2. React 18の「自動バッチ処理(Automatic Batching)」ってなに?
さて、ここからもう少しだけレベルアップします。
React 18(現在の主流のバージョンです)からは、「自動バッチ処理(Automatic Batching)」という機能が標準で搭載されています。
「バッチ(Batch)」って聞き慣れない言葉ですよね。これは、工場でよく使われる「まとめて処理する」という意味の言葉です。
バッチ処理がない世界(昔のイメージ)
もしバッチ処理がなかったらどうなるでしょう?
あなたがボタンを1回ポチッと押したときに、もし裏で3つの状態(お名前、年齢、ログイン状態)を同時に更新していたら、Reactは「お、名前が変わった!再描画!」「年齢が変わった!再描画!」「ログイン状態が変わった!再描画!」と、1回のクリックで3回も画面を塗り替える作業(レンダリング)をしていたんです。
これだと、アプリの動作がカクカクしたり、重くなったりしてしまいますよね。
React 18がやってくれること
そこでReact 18の自動バッチ処理の登場です。
Reactはこう考えました。
「ちょっと待てよ。1回クリックされたときに、どうせ複数の状態が変わるんだから、バラバラに処理しないで、ぜーんぶまとめて1回だけ画面を書き換えれば効率がいいじゃん!」
これがバッチ処理です。私たちが意識しなくても、Reactが賢く「ちょっと待って、まとめて片付けちゃおうね」と調整してくれているのです。
—
3. つまずきポイント!「あれ、値が増えない?」の実例と対策
この「すぐには反映されない」「まとめて処理される」という性質があるせいで、初心者の私たちがよくやってしまう罠があります。
例えば、「ボタンを押したら、カウントを『2』増やしたい!」と思って、こんなコードを書いたとします。
import { useState } from ‘react’;
export default function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
// 2回呼んだから、「0 + 1 + 1」で「2」になるはず……?
setCount(count + 1);
setCount(count + 1);
};
return (
現在のカウント: {count}
);
}
さあ、このボタンを押すと、画面の数字はいくつになると思いますか?
「そりゃあ、2ずつ増えるでしょ!」と思いますよね。
実はこれ、何度押しても「1」しか増えません。
なぜでしょう?
Reactが裏でやっていることを分解してみましょう。
1. `handleClick` が実行された瞬間、変数 `count` の中身はまだ `0` です。
2. 1つ目の `setCount(count + 1)` は、「0 + 1 だから、次にするときは 1 にしてね」というメモを書き残します。
3. 2つ目の `setCount(count + 1)` も、まだ `count` は `0` のままなので、「0 + 1 だから、次にするときは 1 にしてね」というメモを上書きします。
4. 結果として、Reactは「あ、最終的に 1 にすればいいんだな」と解釈して、1しか増えないのです。
解決策:「アロー関数(コールバック関数)」を使おう!
「じゃあ、2つ増やすにはどうしたらいいの!?」ですよね。
ここで登場するのが、Reactの `setState` に関数を渡すテクニックです。
import { useState } from ‘react’;
export default function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
// 現在の状態(prevCount)を確実に受け取って計算する
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
};
return (
現在のカウント: {count}
);
}
このように、`setCount(前回の値 => 新しい値)` という書き方(関数型アップデートと言います)をすると、Reactのメモ帳はこう変わります。
1. 1つ目の処理:「今の値に 1 を足す」
2. 2つ目の処理:「さっき足された後の値に、さらに 1 を足す」
これで、見事に「+2」が実現できます!
「直前の正確な状態を使って計算したいな」というときは、このアロー関数を使うのが実務でも鉄則になっています。
—
4. まとめ:Reactの優しさを信じてあげよう
今日のまとめです。
- Reactの状態更新は「非同期(すぐには反映されない)」
- 注文メモを受け取るだけで、その場ですぐに変数の値が書き換わるわけではないよ。
- React 18の「自動バッチ処理」
- アプリをサクサク軽くするために、複数の状態更新をまとめて1回で処理してくれる優秀な機能だよ。
- 連続で計算したいときは関数を渡そう
- `setCount(prev => prev + 1)` のように書くことで、古いデータを参照してしまうバグを防げるよ。
最初は「なんでストレートに変数が変わってくれないんだ!」とモヤモヤするかもしれませんが、これはすべてアプリのパフォーマンスを落とさないためのReactの優しさなんです。
仕組みさえ分かってしまえば、Reactはあなたの最高の相棒になってくれます。
もしコードを書いていて「あれ、値がおかしいな?」と思ったら、今日のお話を思い出してみてくださいね。
あなたのReactの学習が、もっと楽しく、ワクワクするものになりますように。それでは、また次の記事でお会いしましょう!

コメント