React Router 6.4 メモ
React Router 6.4 になって loader という概念が生え、ルーティングとデータフェッチが密結合になった
これがどうやって実現されているかや、他の新機能についてのメモ
いきなり脱線するけど useRouteError を使うと ErrorBoundary を簡単に実装できる
useLoaderData の定義はここ
DataRouterStateContext
を useContext している
DataRouterStateContext
は <RouterProvider />
で呼び出されている(この表現あってる?)
<RouterProvider />
以下であれば useLoaderData
を呼び出せる
<DataRouterStateContext.Provider />
の初期値は useSyncExternalStoreShim
が返した state が設定されている
ExternalStore
が loader
が fetch するデータと解釈して良さそう
useSyncExternalStore
について
createBrowserRouter
は色々あって、最終的には createRouter
を呼び出す
この中の startNavigation
時に handleLoaders
を呼び出していて、ここで loader
の処理をしていそう
関係ないけど X-Remix-Revalidate
ってヘッダーを見てなんかしてる
最終的に loader
と action
に登録した関数は callLoaderOrAction
の中で呼び出されている
loader
によって得られた result or error は completeNavigation
に渡される
completeNavigation
では updateState
が呼び出される
updateState
では subscribers に state が更新されたことを通知する
更新された state は先に紹介した DataRouterStateContext
経由で、useLoaderData
や useRouteError
で取得できる
loader
や action
の嬉しさはこんな感じ?
- 普通に使う分には
loader
関数の中は普通の TS のコード- React 非依存で嬉しい
- SWR を使っていたときは↓のようなコードを書いていた
- error と loading の UI の表示は手続き的だし規約として強制できていない
- うっかり error の表示の実装を忘れてしまうかも
- error の表示は
errorElement
を設定するという宣言的な書き方かつ規約として強制できる
- error の表示は
- loading の方は loader を await するまで navigation を待つのでそもそも不要
- それでも loading を表示したいときは defer を使う
- うっかり error の表示の実装を忘れてしまうかも
- ↑をまとめると、データフェッチ時に「エラーだった場合にエラーを表示すること」と「ロード中にローディング表示をすること」を規約化できて嬉しい
- error と loading の UI の表示は手続き的だし規約として強制できていない
import useSWR from 'swr'
function Profile() {
const { data, error } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
<Await>
が面白い
- 重たい処理を loader で await しないで promise を defer に渡して return する
-
useLoaderData
で取得した promise を<Await>
の resolve を使うと、Suspense
の仕組みを使って解決される- loading 表示をしたいなら
fallback
にコンポーネントを渡せば良い
- loading 表示をしたいなら
- エラーは
useAsyncError
で取得できる
Promise を渡す、という点で use
に似た雰囲気を感じる
使い方メモ
- ナビゲーションとともに取得されてほしいデータは loader に書く
- 重たい fetch は loader で defer するか、従来どおり SWR や React Query などのライブラリに任せる
- 後者のライブラリはちゃんと
Suspense
をサポートしている
- 後者のライブラリはちゃんと
そう考えると、loader には「ナビゲーションとともにデータを取得する機能」があるので loader と React Query などのライブラリは別軸の仕組みとして捉えて良さそう
まさにドキュメントにあるこの記述の通り
In this way, React Router is about timing, where React Query is about caching.
https://reactrouter.com/en/main/guides/data-libs
React Router 用の React Query から Suspense の仕組みを抜いたキャッシュ付きの fetch ライブラリがあると便利そう