React Native Web で React Navigation を使う場合の URL の設定方法
React Native Web を使って、React Native で実装したアプリケーションを Web で発信します。
その際に React Navigation を使って SPA のナビゲーションを用意した場合の、URL 統合のやり方についてのメモです。
昔の React Navigation バージョンでは、Web でも自動的にページ遷移時の URL 書き換えが行われていました。 しかし、いつからか設定が必要になった様子です。
ナビゲーションについて React Navigation を利用している場合に、画面遷移時の URL 統合と URL でのページアクセスができるようにするための設定方法のメモを以下に記載しています。
概要
- 現象
- ドキュメント
- NavigationContainer に linking Props を渡す
- route と URL path の対応を設定
- route がネストしている場合
- Netlify で発信する際、redirect rule の設定が必要
現象
React Navigation を最新にアップデートしたところ Web で開いた際の URL がずっとルートディレクトリのままになりました。 URL を入力して直接各ページにアクセスしてもかならずトップページが表示されてしまう状態になっていました。
もちろん SPA なので、トップページから各要素をクリックすればコンテンツは切り替わるので機能は利用できます。 ただ、画面が切り替わってもURLが変わりません。URL が統合されていないとブラウザのブックマークとか、リンク貼る時とかに困りますよね。
ドキュメント
以下のドキュメントの内容に従って、URL path と route の対応の設定する必要があります。 https://reactnavigation.org/docs/configuring-links/
NavigationContainer に linking Props を渡す
まず、NavigationContainer に linking
という props を追加する必要があります。
ドキュメントでは以下のような説明になっており、prefixes はURLスキーマなどの設定で、config の方が URL path の設定になります。
今回は URL と route の対応づけを行うので config
の設定を追加する必要がありました。
const linking = {
prefixes: [
/* your linking prefixes */
],
config: {
/* configuration for matching screens with paths */
},
};
~~~
<NavigationContainer linking={linking} >
{/* content */}
</NavigationContainer>
route と URL path の対応を設定
上記のlinking の config に以下のようなオブジェクトを渡すのが基本になります。
/feed の URL が Chat スクリーンに対応します。/user の URL は Profile スクリーンと対応します。
こうすると、Profile スクリーンに遷移すると URL が /user になりますし、URL で直接 /user にアクセスすると Profile スクリーンが開きます。
const linking = {
prefixes: [
/* your linking prefixes */
],
config: {
screens: {
Chat: 'feed',
Profile: 'user',
},
},
};
route がネストしている場合
単純な rouet の構造であれば上記で問題ないのですが、ナビゲーションがネストしている場合はもう少し複雑な設定が必要になります。
私のアプリでは Tab ナビゲーションの中に Stack ナビゲーションがあって、その中に Drawer ナビゲーションがあるというネストした形でした。
結論、以下の形になりました。
const linking = {
config: {
screens: {
ReactNative: {
screens: {
ReactNativeDrawer: {
screens: {
ReactNativeHome: "",
},
},
Icons: "Icons",
FlexBox: "FlexBox",
Shadow: "Shadow",
},
},
Web: {
screens: {
WebHome: "WebHome",
SampleImage: "SampleImage",
},
},
},
},
};
ちょっと解説すると、一番親として Tab ナビゲーションがあります。 以下の部分です。ReactNative と Web の route があります。
config: {
screens: {
ReactNative: {
},
Web: {
},
},
},
で、その ReactNative は Stack ナビゲーションです。以下の部分です。
ReactNativeDrawer ・ Icons ・ FlexBox ・ Shadow の route があります。
Icons は /Icons の path が割り当てられています。FlexBox と Shadow も同様です。
ReactNative: {
screens: {
ReactNativeDrawer: {
},
Icons: "Icons",
FlexBox: "FlexBox",
Shadow: "Shadow",
},
},
この中の ReactNativeDrawer も要素は 1つですが Drawer ナビゲーションを使ったネストになっています。ReactNativeHome という route があります。
ReactNativeDrawer: {
screens: {
ReactNativeHome: "",
},
},
これでそれぞれの route に対して、path が設定されます。画面を遷移すると URL も更新されますし、直接 URL でアクセスすると該当の route が開くようになります。
意図通りに動かない時は、ネストの構造や指定の仕方が間違っている場合があります。親の要素から順番に確認していきましょう。
Home 画面の path
今回のアプリでは ReactNativeHome が Home 画面ですので URLを /
にします。
その場合は上記でもしていますが、空文字を設定します。
ReactNativeHome: "",
Netlify で発信する際、redirect rule の設定が必要
今回作ったシステムが、React Navigation を利用したSPAなので、リダイレクト設定が必要になります。
ローカルで動かしている時は正常にURLが解決していたのですが、Netlify にデプロイしたところうまく処理されなくなりました。 ページがあるはずのURLでアクセスすると 404 エラーになる状態でした。
netlify.toml
をプロジェクトのルートに設置することで Netlify の発信の設定ができるので、ここに以下の内容を追加することで解決できました。
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
おわりに
以前は設定なしでも、URLが解決されていたのですが React Navigation のバージョンアップにより設定が必要になっていました。 URL が切り替わらない SPA だと、ブラウザでのブックマークとかで困りますし、アクセスの度にホームから遷移しないといけないのは少し不便です。
ちょっと複雑ですが対応しておけば、あとは React Navigation が制御してくれるので便利です。
GET パラメータの制御などもここで行うことになります。ドキュメントに書いてあるので、必要な場合は一緒に確認しておきましょう。