はじめに
React18がリリースされて1年以上経つわけですが、
React17以前の情報でアップデートが止まっている方や、React初学者向けの内容かと思います。
今回はStrictモードとAutomatic Batchingについて学んでいこうかと思います。
※Transition/Suspenseについては別でまとめる予定です
本題に入る前に
React18がリリースされ、クライアントやサーバ用に公開しているAPIが再設計されております。
React18の機能をしようするためには、まずレンダリングの方法を変更する必要があります。
変更点
React17以前はReactDOM.render
を用いてrootへのレンダリングを行う。
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<App />
, document.getElementById('root'));
React18ではReactDOM.createRoot
を用いてrootへのレンダリングを行う。
import先も'react-dom'
から'react-dom/client'
に変更する必要があります。
現状create-react-app
すると、自動でこちらのレンダリング方法に対応してくれているかと思います。
import React from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<App />
);
もしReact17のレンダリング方法でReact18の機能を使用すると、
こちらのようなエラーがコンソールに表示されると思います。
こちらのエラーが出ている場合は、createRoot()を使うよう修正しましょう。
Strictモードとは
開発中のアプリケーションの問題点を検出してくれるStrictモードですが、React18で新たな機能が追加されました。
Strictモードを簡単に説明すると、将来的にサポートがされなくなる機能や新しい機能によって壊れてしまう可能性がある部分をwarningとして表示してくれるものです。
※あくまで開発サーバーに対して影響するもので、ビルドして本番環境に上げることでこちらの機能の影響はなくなります。
今回追加された機能についてですが、公式のドキュメントから内容を抜粋させていただきます。
React 18 は strict モードに新しい開発時専用のチェックを導入します。この新しいチェックは、コンポーネントが初めてマウントされるたびに、すべてのコンポーネントを自動的にアンマウント・再マウントし、かつ 2 回目のマウントで以前の state を復元します。
言い直す形になってしまうかもしれませんが、
コンポーネント表示(マウント)⇒コンポーネント破棄(アンマウント)⇒再度コンポーネント表示(再マウント)
といった動きをするわけです。
実際にどういった動きをするのか見てみましょう。
Appコンポーネントを作成し、その中でuseEffectを呼び出します。
import React, { useEffect } from 'react';
function App() {
useEffect(() => {
console.log('Strictモード確認');
}, []);
return (
<div className="App"></div>
);
}
export default App;
React18の場合、useEffectが2回呼び出されるので、ログが2回表示されます。
マウント⇒アンマウント⇒再マウントが行われているのが見て取れたと思います。
こちらの機能追加の目的としては、将来的に実装が予定されているOffscreen APIのための布石が主となっています。
Offscreen APIとは、同じstateを使用してツリーをアンマウント・再マウントすることで高速な画面表示を行えるようにする機能です。
Offscreen APIが実際に実装された際に、すぐ適応できるような設計を今から心がけようということです。
StrictモードをOFFにする方法はあるか?
React18にすることで、こちらの挙動は必然的に追加されますので、
<React.StrictMode>
をコメントアウトするしかないと思います。
Automatic Batching
バッチ処理に関しては公式から引用させていただきます。
バッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します。自動バッチング以前は、React のイベントハンドラ内での更新のみバッチ処理されていました。promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。
React18以前では、promiseやsetTimeoutの中でstateの更新が行われるとその度に再レンダリングが実行されていました。
Automatic Batchingにより、promiseやsetTimeoutの中で何度stateの更新を行っても再レンダリングされるのは1度だけになったというわけです。
実際の動きはこちらになります。
APIを使うので、json placeholderというサービスを利用します。
import { useState } from 'react';
type Todo = {
"userId": number,
"id": number,
"title": string,
"completed": boolean
}
export const AutoBatchOther = () => {
console.log('レンダリングされてる!');
const [todos, setTodos] = useState<Todo[] | null>(null);
const [state2, setState2] = useState<string>('');
const onClickExecutionApi = () => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then((res) => res.json())
.then((data) => {
setTodos(data);
setState2('updated');
})
}
return (
<div>
<p>Automatic Batch確認</p>
<button onClick={onClickExecutionApi}>API実行</button>
{todos?.map((todo) => <p key={todo.id}>{todo.title}</p>)}
</div>
)
}
React17の場合、API実行ボタンを押すとsetTodosとsetState3の2回再レンダリングが行われているのが分かると思います。
React18になると、Automatic Batchingにより再レンダリングが1回だけになっていると思います。
Automatic Batchingは実装者が特に意識しなくても、バージョンを上げるだけで適応されるので、
ソースを修正する必要もないし、パフォーマンスは上がるしでメリットしかないですね!
最後に
Reactについてはudemyを中心に勉強していますが、
観て、ソースコード書いて、アウトプットしてといった流れにしないとすぐ忘れてしまいます...
自分も初学者なので間違っている点などありましたら、ご指摘ください。
参考記事
React 公式
今後のフロントエンド開発で必須知識となるReact v18の機能を丁寧に理解する
テスト用APIサーバー