title | emoji | type | topics | published | ||
---|---|---|---|---|---|---|
React18の新機能(Automatic Batching)の使い方を確認 |
🎃 |
tech |
|
true |
今回は React18 の新機能Automatic Batchig
について React17 と React18 それぞれの動作の違いについて確認していきたいと思います。
Transition についてはこちらの記事をご覧ください。
https://zenn.dev/rh820/articles/686701ba31ed44
https://github.com/dahoho/react-18-demo
React 18 では、デフォルトでより多くのバッチ処理を実行することで、すぐに使用できるパフォーマンスの向上が追加されています。バッチ処理とは、パフォーマンスを向上させるために、React が複数の状態更新を 1 つの再レンダリングにグループ化することです。React 18 より前は、React イベント ハンドラー内で更新をバッチ処理するだけでした。Promise、setTimeout、ネイティブ イベント ハンドラー、またはその他のイベント内の更新は、デフォルトでは React でバッチ処理されませんでした。
(引用元:https://react.dev/blog/2022/03/08/react-18-upgrade-guide#automatic-batching)
Automatic Batching は、複数の setState などが呼ばれた場合に、一度のレンダリングでまとめて処理する機能です。 React17 までは、関数内でのみバッチ処理が行われていましたが、React18 からは関数外でもバッチ処理が行われるようになりました。 promise や setTimeout、ネイティブイベントハンドラーなどの関数外での処理もバッチ処理が行われるため、パフォーマンスが向上しました。 まずは、React17 でも行われている関数内でのバッチ処理を確認していきます。
:::message 今回は動作確認をしやすくするために Strict モードを無効にしています。 :::
更新ボタンをクリックすると、コンソールに foo の値が 2 回出力されるコンポーネントです。
import { useState } from "react";
const EventHandler = () => {
console.log("EventHandlerコンポーネントがレンダリングされました!");
const [foo, setFoo] = useState<number>(0);
const [bar, setBar] = useState<number>(0);
const countUp = () => {
console.log(foo);
setFoo((prev) => prev + 1);
console.log(foo);
setBar((prev) => prev + 1);
};
return (
<div className="mt-8">
<p className="text-center">イベントハンドラ内の処理</p>
<div className="mt-6 text-center">
<div className="flex justify-center gap-5">
<p>foo: {foo}</p>
<p>bar: {bar}</p>
</div>
<button
className="bg-slate-600 text-white py-2 px-5 mt-5"
onClick={countUp}
>
更新
</button>
</div>
</div>
);
};
export default EventHandler;
コンソールを確認すると、以下のようになります。 ボタンをクリックすると、同じ値が 2 回出力された後にコンポーネントが再レンダリングされることが確認できます。 これによって、setState がバッチ処理(1 つにまとめられている)されていることがわかります。 バッチ処理が行われない場合、setState の記述を増やすごとにコンポーネントが再レンダリングされてしまい、パフォーマンスが低下します。
関数外でのバッジ処理は React18 からの新機能です。
fetch で jsonplaceholder からデータを取得して表示するコンポーネントで確認していきたいと思います。
バッチ処理が行われているか確認するため、分かりやすいようにsetIsVisible((prev) => !prev);
を 3 つに増やして、React17 と React18 での挙動の違いを確認していきたいと思います。
import { useState } from "react";
type TodoType = {
userId: number;
id: number;
title: string;
completed: boolean;
};
const Other = () => {
console.log("Otherコンポーネントがレンダリングされました!");
const [todosData, setTodosData] = useState<TodoType[] | null>(null);
const [isVisible, setIsVisible] = useState<boolean>(false);
const fetchApi = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
const data = await res.json();
setIsVisible((prev) => !prev);
setIsVisible((prev) => !prev);
setIsVisible((prev) => !prev);
setTodosData(data);
};
return (
<div className="mt-8">
<p className="text-center">イベントハンドラ以外の処理</p>
<div className="mt-6 text-center">
<button
className="bg-slate-600 text-white py-2 px-5 mt-5"
onClick={fetchApi}
>
API取得
</button>
{isVisible && (
<div className="mt-8">
{todosData?.map((todo) => (
<p key={todo.id} className="p-2">
{todo.title}
</p>
))}
</div>
)}
</div>
</div>
);
};
export default Other;
コンソールを確認すると、以下のようになりました。 React17 では、関数外でのバッチ処理は行われていないため、setState の回数だけコンポーネントが再レンダリングされていることがわかります。 一方、React18 では、関数外でもバッチ処理が行われているため、コンポーネントが無駄に再レンダリングされていないことがわかります。
React17 と React18 のそれぞれの関数内バッチ処理と関数外でのバッチ処理の違いを確認しました。 自動バッチングは普段開発で意識しなくても有効化されている機能ですが、理解しておくべき機能だと思います。 今後は Transition や Suspense などの機能についても確認していきたいです。
https://ja.legacy.reactjs.org/blog/2022/03/29/react-v18.html https://www.udemy.com/course/react_v18/