5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Batch Update State trong React 18

5
Last updated at Posted at 2025-11-17

React 18におけるバッチ更新ステートの仕組みを理解する

Reactでは、setState(クラスコンポーネント)やuseState(関数コンポーネント)のセッター関数でステートを更新するたびに、Reactは再レンダリングをスケジュールします。複数のステート更新が連続して呼び出されると、複数回のレンダリングが無駄になり、アプリケーションが遅くなります。最適化のため、Reactはバッチ更新ステートの仕組みを使用します—複数のステート更新をまとめて、1回のレンダリングのみで処理します。

この記事では、バッチングの仕組みをシンプルで分かりやすく説明し、関数更新ステートとその利点に焦点を当てます。


1. バッチングとは?

バッチングとは、Reactが単一の「イベント」または「タスク」内で複数回のセッター関数(setCountなど)の呼び出しをまとめて、1回のレンダリングのみで処理することです。

クラスコンポーネントの例(旧仕組み):

// 初期値 count = 0 と仮定
increment() {
  this.setState({ count: this.state.count + 1 });
  this.setState({ count: this.state.count + 1 });
  this.setState({ count: this.state.count + 1 });
}
// 結果: count = 1、レンダリングは1回のみ

理由: すべての呼び出しが現在のレンダリング時点のthis.state.countの値を読み取るため、最終的なcountは1になります。

関数コンポーネントと関数更新の例:

// 初期値 count = 0 と仮定
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
// 結果: count = 3、レンダリングは1回のみ

関数更新により、バッチ内の最新値に基づいて各更新が保証されます。

React Docs: State and Lifecycle


2. React 18と自動バッチング

React 18以前では、バッチングはReactが管理するイベント(onClickonChange)内でのみ発生していました。非同期タスク(setTimeout、fetch)内のステート更新はバッチ処理されませんでした。

React 18以前の例:

handleClick = () => {
  setTimeout(() => {
    setCount(c => c + 1);
    setFlag(f => !f);
  }, 100);
};
// React 18以前: 2回の個別レンダリング

React 18は自動バッチングをすべてのケースに拡張しました:

  • setTimeout、setInterval
  • 非同期コールバック
  • Promise .then().catch()
  • ネイティブイベントリスナー

React 18での例:

handleClick = () => {
  setTimeout(() => {
    setCount(c => c + 1);
    setFlag(f => !f);
  }, 100);
};
// React 18: 2つのステート更新がバッチ処理され、レンダリングは1回のみ

React Docs: Automatic Batching


3. 関数更新ステートとは何か、その利点

関数更新ステートとは、現在のステート値を受け取り、新しいステートを返す関数でセッター関数を呼び出す方法です。

setCount(prevCount => prevCount + 1);

利点:

  1. 最新のステートに基づく計算を保証: 複数の連続した更新がある場合。
  2. 「Stale Closures」を回避: 非同期タスク内で、ステートが常に最新になります。
  3. バッチ更新に対して安全: バッチ処理の有無に関わらず、結果は常に正確です。

例:

const [count, setCount] = useState(0);
useEffect(() => {
  const id = setInterval(() => {
    setCount(prevCount => prevCount + 1);
  }, 1000);
  return () => clearInterval(id);
}, []);
// `count`を依存配列に追加する必要はありません

React Docs: Functional Updates


4. バッチングが発生しない場合

  • flushSyncを使用して即座にレンダリングする場合:
import { flushSync } from 'react-dom';
flushSync(() => {
  setCount(c => c + 1);
});
setFlag(f => !f); // 2回目のレンダリング

パフォーマンスに悪影響を与える可能性があるため、特別な状況でのみ使用してください。


5. パフォーマンスと再レンダリング

バッチングにより、不要なレンダリング回数が減ります。React.memouseMemouseCallbackと組み合わせて、子コンポーネントを最適化します。

例:

import { memo, useState } from 'react';
const Child = memo(({ text, user }) => <div>{text} - {user.name}</div>);
function Parent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('Hello');
  const [user, setUser] = useState({ name: 'John' });

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <Child text={text} user={user} />
    </>
  );
}

注意: React.memoは浅い比較(shallow)を行います。レンダリングごとに新しいオブジェクトを渡すと、コンポーネントは再レンダリングされます。


6. ベストプラクティス

  1. 新しいステートが古いステートに依存する場合は、常に関数更新を使用:
setCount(c => c + 1);
  1. 関連する複数のステートをオブジェクトにまとめるか、useReducerを使用:
setUser(prev => ({ ...prev, name: 'New Name', age: 30 }));
  1. memo、useMemo、useCallbackで子コンポーネントを最適化:
  • 重いコンポーネントや、propsが頻繁に変わらない場合に使用。
  1. useEffectの依存配列を理解する:
// GOOD: 関数更新により、`count`を依存配列に追加する必要はありません
useEffect(() => {
    const id = setInterval(() => {
        setCount(c => c + 1);
    }, 1000);
    return () => clearInterval(id);
}, []); // 空の依存配列

// BAD: `count`を依存配列に追加すると、エフェクトのクリーンアップとセットアップが毎秒実行されます
useEffect(() => {
    const id = setInterval(() => {
        setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
}, [count]);

関数更新により、不安定な依存関係を省略できます。


7. 結論

バッチ更新ステートは以下を実現します:

  • 不要な再レンダリングを削減。
  • アプリケーションのパフォーマンスを向上。
  • ステートの一貫性を保証。

React 18の自動バッチングと関数更新により、開発者は安全で効率的なコードを書くことができます。

参考:

5
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?