Remix で SearchParams を変更しただけだと defer、 <Suspense>、<Await> のストリーミングが機能しない
に公開背景
テーブル表示で loader の defer 機能と <Suspense> <Await> を使ってロード中はスケルトンを表示したかった。
初回ロード時は問題なくロード中はスケルトンが表示されていたが、テーブルのソート順を SearchParams の sortKey として持っていて、テーブルのカラムをクリックしたときに同じパスで SearchParams の sortKey だけを変更した場合、スケルトンが表示されず、loader からデータが返された瞬間に再レンダリングされるような挙動になっていた。
解決方法
useNavigation の navigation.location がある場合とない場合で場合分けして、navigation.locaton があるときはスケルトン表示、ないときは <Suspense>、<Await> する。
export function Index(){
const loaderData = useLoaderData<typeof loader>();
const navigation = useNavigation();
// navigation.location がある場合は遷移中なので skeleton 表示
if (navigation.location){
return <Table skeleton />
}
return (
<Suspense fallback={<Table skeleton />}>
<Await resolve={loadData}>
{(resolvedValue) => (
<Table data={resolvedData} />
)}
</Await>
</Suspense>
)
}
補足
以下の issue を参考。
defer not working when navigating to the same route with different url search/params #6637
ただこれだと複数の Promise を返す場合、すべてが解決されないとスケルトンが解除されないので、ストリーミング機能はうまく使えてないような気がする。