【入門編】 React状態更新の非同期性とバッチ処理 – React実践ガイド

こんにちは!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の学習が、もっと楽しく、ワクワクするものになりますように。それでは、また次の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました