はじめに
SWRはバージョン2.0.1でSuspenseモードにおけるdata
の型推論の結果が変化しました。パッチリリースですが、長年苦しんでいた問題が解決されたので紹介します。
2.0.0以前の世界
ReactにSuspenseが登場してからデータ取得はより宣言的に書くことができるようになりました。
元々SWRを用いてデータ取得を行うコンポーネントは以下のように書かれていました。以後fetcher
の返り値はPromise<string>
とします。
export const Sample = (): JSX.Element => {
const { data } = useSWR(key, fetcher);
if (!data) {
return <Loading />;
}
return <>{data}</>;
};
そして、Suspenseが登場してからは以下のように書けるようになりました。
export const Sample = (): JSX.Element => {
const { data } = useSWR(key, fetcher, {
suspense: true,
});
return <>{data}</>;
};
export const App = (): JSX.Element => {
return (
<Suspense fallback={<Loading />}>
<Sample />
</Suspense>
);
};
data
が取得できていなかったら(undefined
だったら)<Loading />
を返すような命令的なコードから親コンポーネントで<Sample />
がSuspenedされていたらfallback
を表示させるような宣言させるコードに変わっています。
この例ではuseSWR
からdata
が返される時にはfetcher
が解決済みになっているはずなので、data
の型はstring
となって欲しいです。しかし、実際はstring | undefined
となります。この結果はこの例では問題になりませんが、取得したデータを他のコンポーネントに渡すときや、プリミティブで無いデータを扱っているときにdata
がundefined
ではないことを確認する分岐が必要になってしまいます。
このことはswrのリポジトリでも議論に挙げられることが多く、例えば以下のissueで議論されていました。
2.0.1で変更された型
先日のパッチバージョンアップではその問題が解決しました。
export const Sample = (): JSX.Element => {
const { data } = useSWR(key, fetcher, {
suspense: true,
});
return <>{data}</>;
};
export const App = (): JSX.Element => {
return (
<Suspense fallback={<Loading />}>
<Sample />
</Suspense>
);
};
先ほどと同じコードなわけですが、data
の型はstring | undefined
からstring
に推論されるように変更されました。
これによって毎回data
がundefined
では無いことを確認しなくて済むようになったのでとても嬉しい変更です。ただし、useSWR
の第三引数options
に{ suspense: true }
としなければこの推論にならないので注意してください。
疑問
私の環境ではSuspenseモード下で条件付きフェッチを行ったとしてもdata
の型はstring
に推論されてしまいました(実行するとエラーが出ます)。
ドキュメントに従うとdata
の型はstring | undefined
になると考えていたので疑問でした。
先ほどのissueを読むとSuspenseが有効な時はdata
がundefined
にならないという制限だけではなく、key
がFalsyな値ではないこと、fetcher
がnull
ではないような制限が必要と書かれていました。SuspenseモードではPromise
をthrow
するもしくは解決後の値を返すだけを想定していて、これに当てはまらない条件付きフェッチではエラーを出さざるを得ないのではいかなと考えています。
今後この周りがどのように変化していくかとても楽しみです。