React Router v6 がリリースされてから凡そ半年が立ちました。
そして、v6.4 の足音が近づいてきています。 まだ v5 を利用している方は、これを機にバージョンを上げてみませんか?
上げたくても上げられない
もちろん訳あって上げられない場合もあるかと思います。
私が参加しているプロジェクトでも、流行に遅れてつい先日まで v5 でした。 なぜなら、v6 には preload API がなかったのです。*1
これは、Render-as-you-fetch パターンを導入しているアプリケーションにとって v6 バージョンアップへのハードルを上げていました。
v5 では、以下のように Route コンポーネントが持つ render prop*2 からデータ取得(Relay の loadQuery など)を挟んだ関数を渡すことができました。
<Route path="/home" render={() => { const queryRef = loadQuery(environment, homeQuery, ...) return <Home queryReference={queryRef} ... /> }} />
そして v6.4 では、これを解決した loader prop がやってきます。 それを提供してくれるのが、DataBrowserRouter です。
現時点(2022/06/19)ではまだ beta ですが、公式ドキュメントが用意されているので見てみてください。 Data Quick Start | React Router
以下のように、loader prop に渡した関数が返すデータを useLoaderData フックを使って取得することができます。
// routing ... <Route element={App} loader={getAppData} /> // App.tsx const data = useLoaderData() return <div>{data.name}</div>
はい簡単ですね。ではサクッと v5 から脱出してしまいましょう。
React Router v6.4 と遷移アニメーション
しかし、然うは問屋が卸さないのがメジャーバージョンアップの作業です。
例として、v5 の Switch コンポーネントと framer-motion を組み合わせて、ルーティング遷移にアニメーションを実装している場合を考えてみます。 (もちろん render func でデータ取得も行っているとします)
<AnimatePresence> <Switch location={location} key={...} > <Route path="/" ... /> <Route path="/:id" ... /> </Switch> </AnimatePresence>
さて、これを v6 用に直すと以下のように書けます。
<AnimatePresence> <Routes location={location} key={...} > <Route path="" ... /> <Route path=":id" ... /> </Routes> </AnimatePresence>
Switch を Routes に置き換えるだけです。はい簡単ですね。
しかし、DataBrowserRouter は Route コンポーネントしか受け付けていません。そうです、Routesコンポーネントが使えないのです。 Switch を使ってアニメーションを行っていた場合は Route コンポーネントのみで実装する必要があるのです。
ここで、React Router v6 で追加された Outlet という機能が生きてきます。これを使うと、コンポーネントとルーティングの実装を分離させることができます。
先程 v6 用に書き直したものをさらに DataBrowserRouter とアニメーションを組み合わせた、v6.4 向けに書き直してみます。
// routing <Route path="*" element={Wrapper} > <Route path="" element={...} /> <Route path=":id" element={...} /> </Route> // Wrapper.tsx const Wrapper = () => { const outlet = useOutlet() return ( <AnimatePresence> <div key={location.pathname}> {outlet} </div> </AnimatePresence>
上記のように、useOutlet が返すものを div や Suspense で囲って location.pathname を key に渡してあげると、 ルーティング遷移のタイミングでアニメーションを行うことができます。
注意
location が変化すると、ルーティング定義に応じて loader prop も変わるので useLoaderData が返す値も変化してしまいます。 そのため、遷移アニメーションでは旧ページが unmount されるまで useLoaderData が返す値をキャッシュしておく必要があります。
次のデモアプリでは useLoaderData を wrap した、カスタム hook を用意しました。*3
デモ
上記の実装を用いて簡単なアニメーション付きのアプリを作ってみたので参考にしてみてください。
さあ、あなたも React Router v5 から脱出しませんか?
参考資料
- React Router | Upgrading from v5
- 大きく機能が変わる?React Router v6.4 Pre-releaseをためしてみた | DevelopersIO
- react-router-v6-animated-routes - CodeSandbox
- Comments on the v6 preload API · Discussion #8009 · remix-run/react-router
- [v6] First-class animation primitives · Discussion #8008 · remix-run/react-router
- [v6] [Feature]: Guidance on animating out routes · Discussion #8604 · remix-run/react-router
- ergofriend/react-router-loader-and-relay-preload