Next12+React18にて、Streaming SSRを用いてSuspenseを行う際の注意点みたいな話です。
Reactは全体で一貫性を保つために、内包するコンポーネントがsuspendした場合は全てのレンダリングを行いません。
これは非同期処理を用いたコンポーネントが正しくPromiseをThrow出来なかったり不正な値をreturnした場合などに、全体のレンダリングを中断する機能です。
これによりページが部分的に破損してしまうなんてことはなくなりますが、逆に言えば一箇所がコケるだけで全てが見えなくなってしまいますので、注意が必要です。
注意点1
また、これは公式ドキュメントにPitfallとして書かれている情報で、独自の手段によるデータのfetchはSuspenseは気が付かないよ、ということが書かれています。具体的にはuseEffect内部で発生させたPromiseなどです。
const [data, setData] = useState(null)
useEffect(() => {
FetchData().then(setData)
}, [])
一見すると動作するのですがSuspenseは効いていないのでコンポーネントがPromiseを投げられる恩恵を受けられていません。意味が無いです。
注意点2
また、APIデータをuseStateなどのHooksで持たせる場合にも注意が必要です。Hooksはコンポーネントのレンダリングが完了しない限り、毎回再生成されます。これが意味することは、無限ループの発生を招く危険性があるということです。具体的には以下のような実装で起こり得ます。
const [data, setData] = useState(null)
if (data === null) {
throw FetchData().then(setData)
}
SWRを使う
実装する手段の1つとして、SWRを使うと良いです。SWRはSuspenseをサポートしています。この様に定義されたComponentであればSuspenseでラップすることでその恩恵を正しく受けることが出来ます。
// Data.tsx
import useSWR from 'swr'
const { data } = useSWR('endpoint', fetcher, option)
/* render components with data */
// index.tsx
import { lazy, Suspense } from 'react'
const Data = lazy(() => import('../components/Data'))
<Suspense fallback={<Loading />}>
<Data />
</Suspense>
また、useSWRを多用する場合はカスタムフックにしてあげることでnullやundefinedを隠蔽してあげることが出来ます。必要に応じて抽象化していきましょう。
Top comments (0)