さて、今更感ありますが
React18の理解をより深めるため、今回はこの2つテーマに選びました。
知ってるけど実際に試していない!ふわっとした理解のままだった!というかたは是非動かして試してくださいね。
AutomaticBatching オートマチックバッチング
イベントハンドラーの中で複数のstateを更新する時、レンダリングを1回にまとめてくれる機能となります。
こちらはReact17にて、すでに存在した機能です。
React18で変わった点はというと、Priomiseの内部で行われる処理に対してもbatchingが動くようになった...
ということで、試してみます。
const AutomaticBatching = () => {
const [user, setUser] = useState(0)
const [count, setCount] = useState(0)
const clickHandler = () => {
axios.get('https://jsonplaceholder.typicode.com/users').then((res) => {
setUser(res.data)
setCount((count) => count + 1)
})
}
console.log('Automatic Batching')
console.log(user)
console.log(count)
return (
<div className={styles.center}>
<div>
<button onClick={clickHandler}>clickHandler</button>
</div>
</div>
)
}
export default AutomaticBatching
適当にpageを作成し、動かしてみます。
(画質悪くて申し訳ないですが)
ボタンを押してstateを2つ更新しましたが、レンダリングは1度だけだと確認できました。
React17までの挙動を再現することもできます。
先ほどのコードに flushSync
をimportし、非同期内で囲うだけでOK。
import { flushSync } from 'react-dom' //追加
const clickHandler = () => {
axios.get('https://jsonplaceholder.typicode.com/users').then((res) => {
flushSync(() => {
setUser(res.data)
})
flushSync(() => {
setCount((count) => count + 1)
})
})
}
Automatic Batchingが無効化されて、React17までと同じように2回レンダリングされるようになりました。
Suspense(Concurrent Rendering)
Suspenseはは、データを取得するコンポーネントが非同期にデータをロードするのを待ち、データが利用可能になったら表示を切り替える機能を提供します。
ルールは2つで
- SuspenseさせたいコンポーネントはPromiseをthrowすること
- ↑をSuspenseコンポーネントで囲い、fallbackを指定すること
// useSWR,axiosを使用して、1ファイルで行ってしまいますね
import { Suspense } from "react";
import useSWR from "swr";
import axios from "axios";
async function sleep(ms: number) {
return await new Promise((resolve) => setTimeout(resolve, ms));
}
const fetcher = (url: string) => axios.get(url).then((res) => res.data);
const useFetch = (url: string) => {
// ↓無理やりPromiseをthrowして、時間かせぎもしてみてね
// if (Math.random() < 0.5) {
// throw sleep(1000);
// }
const { data, error } = useSWR(url, fetcher, { suspense: true });
return { data, error };
};
const UserEmail = () => {
const { data, error } = useFetch(
"https://jsonplaceholder.typicode.com/users/1"
);
if (error) return <p>取得失敗</p>;
return <div>Data is {data.email}</div>;
};
export const RenderAsYouFetch = () => {
return (
<div className="text-center">
<Suspense fallback={<p>Loading...userEmail</p>}>
<UserEmail />
</Suspense>
</div>
);
};
Render-as-You-Fetchパターンについて
データをリクエストしている間にコンポーネントのレンダリングを開始し、データがまだ利用できない場合はフォールバックUIを表示すること。
useSWR
をsuspense: true
し、データがまだ利用できない間、SuspenseがfallbackのUI
を表示するようにしています。
Suspenseを使わない場合は、
const UserEmail = () => {
~~
if (error) return <div>Error...</div>;
if (!data) return <div>Loading...</div>;
~~
としてあげてもRender-as-You-Fetchパターン
です。
まとめ
今回は AutomaticBatching
と Suspense
について実装しながら理解を深めました。その中でuseSWR
やRender-as-You-Fetchパターン
も使うことができました。
引き続き、Reactの機能を深ぼるぞ!
参考
-
ReactのSuspense対応非同期処理を手書きするハンズオン :
- Suspenseの挙動を0から追っていくのでかなりわかりやすい。
- アンチパターンも記載されておりました。