Boundaryを意識せずにsuspendをすると世界は消滅するしHooksはSuspenseできない

jpnykw

Haruto Hirakawa

Posted on November 4, 2022

Boundaryを意識せずにsuspendをすると世界は消滅するしHooksはSuspenseできない

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)
}, [])
Enter fullscreen mode Exit fullscreen mode

一見すると動作するのですがSuspenseは効いていないのでコンポーネントがPromiseを投げられる恩恵を受けられていません。意味が無いです。

注意点2

また、APIデータをuseStateなどのHooksで持たせる場合にも注意が必要です。Hooksはコンポーネントのレンダリングが完了しない限り、毎回再生成されます。これが意味することは、無限ループの発生を招く危険性があるということです。具体的には以下のような実装で起こり得ます。

const [data, setData] = useState(null)
if (data === null) {
  throw FetchData().then(setData)
}
Enter fullscreen mode Exit fullscreen mode

SWRを使う

実装する手段の1つとして、SWRを使うと良いです。SWRはSuspenseをサポートしています。この様に定義されたComponentであればSuspenseでラップすることでその恩恵を正しく受けることが出来ます。

// Data.tsx
import useSWR from 'swr'
const { data } = useSWR('endpoint', fetcher, option)
/* render components with data */
Enter fullscreen mode Exit fullscreen mode
// index.tsx
import { lazy, Suspense } from 'react'
const Data = lazy(() => import('../components/Data'))

<Suspense fallback={<Loading />}>
  <Data />
</Suspense>
Enter fullscreen mode Exit fullscreen mode

また、useSWRを多用する場合はカスタムフックにしてあげることでnullやundefinedを隠蔽してあげることが出来ます。必要に応じて抽象化していきましょう。

💖 💪 🙅 🚩
jpnykw
Haruto Hirakawa

Posted on November 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related