Reactでデータフェッチをする際にSuspenseを使おう!!
本記事で使用する技術
- Reactv18~
- TypeScript
- 気象庁のお天気API
導入
みなさんはReactでデータフェッチを行い、その間にローディング画面を表示させるといったものを実装した経験あると思います。
例えば、下記のようなuseEffectを用いた実装方法が挙げられると思います。
useEffectを用いたデータ取得方法
import { useEffect, useState } from 'react'
const App = () => {
const [weather, setWeather] = useState<any>(null)
const getWeather = async () => {
const res = await fetch(`https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json`)
const weatherData = await res.json()
setWeather(weatherData)
}
useEffect(() => {
getWeather()
}, [])
return (
<div>
{weather ? (
<div>
<p>{weather[0].publishingOffice}</p>
</div>
): (
<p>loading...</p>
)}
</div>
)
}
export default App
しかし、React18である現在、他にもさまざまな方法での実装が可能となっています。useEffectでのデータフェッチを行っているという方はぜひ見ていただけたら嬉しいです。
Suspenseへの招待
Suspense
とはReactが提供しているコンポーネントで、データフェッチを待ってくれるものです。
Suspense
で囲われているExample
のデータが取得できるまでは、fallback内がレンダリングされ、完了するとExample
がレンダリングされます。
<Suspense fallback={<p>Loading...</p>}>
<Exmaple />
</Suspense>
どういう仕組みなのか
下記のコードを見てもらえればわかりますが、Promiseをthrowすることによって実現しています。
throw
と聞くと反射でエラー処理を思い浮かべる方も多くいるかと思いますが、throw
はPromiseに使うこともできます。
throw
されたpending状態のPromiseはSuspense
によって捕まえられ、Suspense
はfallback内のものをレンダリングします。
データが取得できたあとは、if文により、Promiseはthrow
されず、returnの中身がレンダリングされます。
イメージ図
Suspenseコード見本
import { Suspense } from 'react';
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
let weatherData: any;
const getWeatherData = async (): Promise<any> => {
//データ取得がわかりやすいように5秒待つ
await sleep(5000);
const res = await fetch(`https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json`)
return res.json();
}
const SuspendWeather = () => {
if(!weatherData){
throw getWeatherData().then((data) => (weatherData = data))
}
return (
<div>
<p>{weatherData[0].publishingOffice}</p>
</div>
);
}
const App = () => {
return (
<>
<Suspense fallback={<p>Loading...</p>}>
<SuspendWeather />
</Suspense>
</>
);
}
export default App;
SWRを使った方法
SWRとは、vercel社が提供するデータ取得関連を簡単に行うことのできるReactのライブラリです。
npm install swr
今回のメインはSuspenseですし、特に説明なしでも理解できると思うので、下記のコードをご覧ください。
import { Suspense } from 'react';
import useSWR from 'swr';
const SuspendWeather = () => {
const fetcher = async(url: string) => {
//データ取得がわかりやすいように5秒待つ
await sleep(5000);
const res = await fetch(url)
return res.json()
}
const { data, error, isLoading } = useSWR(`https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json`, fetcher)
if (isLoading) {
return (
<div>
<p>Loading...</p>
</div>
)
}
return (
<div>
<p>{data[0].publishingOffice}</p>
</div>
);
}
const Home = () => {
return (
<>
<SuspendWeather />
</>
);
}
export default Home;
SWR + Suspenseを使った方法
また、SWRにSuspenseを組み合わせた実装を行うことも可能です。useSWR
の第三引数に{suspense: true}
を指定するだけです。
throw
やPromise
といったものを記述する必要もなく、前節の理解がなくても、天下り的に利用することができます。
import { Suspense } from 'react';
import useSWR from 'swr';
const SuspendWeather = () => {
const fetcher = async(url: string) => {
//データ取得がわかりやすいように5秒待つ
await sleep(5000);
const res = await fetch(url)
return res.json()
}
const { data, error } = useSWR(`https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json`, fetcher, { suspense: true })
return (
<div>
<p>{data[0].publishingOffice}</p>
</div>
);
}
const Home = () => {
return (
<>
<Suspense fallback={<p>Loading...</p>}>
<SuspendWeather />
</Suspense>
</>
);
}
export default Home;
まとめ
最近のフロントエンド界隈のスピードは早く、この前推奨されていたものが、もう非推奨になったり、新しいフレームワークがどんどんでてきたり、追いついていくのに必死だと思います。私もその一人です。今回の記事も全く完璧なものではありませんが、役に立つものであったら嬉しいです。
また、間違いっている部分があったら、指摘いただけら幸いです。
Discussion