本文へスキップ
バージョン: 6.x

リンクの構成

このガイドでは、外部リンクを処理するようにReact Navigationを構成します。これは、次の場合に必要です。

  1. AndroidとiOSのReact Nativeアプリでディープリンクを処理する
  2. Webで使用する場合にブラウザでURL統合を有効にする
  3. パスを使用して移動するには、<Link />またはuseLinkToを使用します。

先に進む前に、アプリでディープリンクを構成していることを確認してください。AndroidまたはiOSアプリをお持ちの場合は、prefixesオプションを指定することを忘れないでください。

NavigationContainerは、受信リンクの処理を容易にするlinkingプロップを受け入れます。linkingプロップで指定できる最も重要なプロパティの2つは、prefixesconfigです。

import { NavigationContainer } from '@react-navigation/native';

const linking = {
prefixes: [
/* your linking prefixes */
],
config: {
/* configuration for matching screens with paths */
},
};

function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
{/* content */}
</NavigationContainer>
);
}

linkingプロップを指定すると、React Navigationは受信リンクを自動的に処理します。AndroidとiOSでは、アプリがリンクで開かれた場合と、アプリが開いているときに新しいリンクを受信した場合の両方で、受信リンクを処理するためにReact NativeのLinkingモジュールを使用します。Webでは、ブラウザのURLと同期するためにHistory APIを使用します。

警告

現在、Androidで解決されないバグ(facebook/react-native#25675)があるようです。永遠にスタックするのを防ぐためにタイムアウトを追加していますが、そのため、場合によってはリンクが処理されない可能性があります。

NavigationContainerfallbackプロップを渡すこともできます。これは、React Navigationが初期のディープリンクURLを解決しようとしているときに表示されるものを制御します。

プレフィックス

prefixesオプションを使用して、ユニバーサルリンクまたはAndroidアプリリンクを構成している場合は、カスタムスキーム(例:mychat://)とホスト&ドメイン名(例:https://mychat.com)を指定できます。

例えば

const linking = {
prefixes: ['mychat://', 'https://mychat.com'],
};

prefixesオプションはWebではサポートされていません。ホストとドメイン名は、ブラウザのWebサイトURLから自動的に決定されます。アプリがWebでのみ実行される場合は、構成からこのオプションを省略できます。

複数のサブドメイン

関連付けられたドメインのすべてのサブドメインと一致させるには、特定のドメインの先頭に*を付けることでワイルドカードを指定できます。アスタリスクの後のピリオドのために、*.mychat.comのエントリはmychat.comと一致しません。*.mychat.commychat.comの両方に一致を有効にするには、それぞれについて個別のプレフィックスエントリを提供する必要があります。

const linking = {
prefixes: ['mychat://', 'https://mychat.com', 'https://*.mychat.com'],
};

特定のパスのフィルタリング

受信リンクをすべて処理したくない場合があります。たとえば、特定の画面に移動する代わりに、認証(例:expo-auth-session)またはその他の目的のためのリンクをフィルターで除外する場合があります。

これを実現するために、filterオプションを使用できます。

const linking = {
prefixes: ['mychat://', 'https://mychat.com'],
filter: (url) => !url.includes('+expo-auth-session'),
};

ページのURLを常に処理する必要があるため、これはWebではサポートされていません。

パスとルート名のマッピング

リンクを処理するには、有効なナビゲーション状態に翻訳する必要があります。たとえば、パス/rooms/chat?user=janeは、次のような状態オブジェクトに変換できます。

const state = {
routes: [
{
name: 'rooms',
state: {
routes: [
{
name: 'chat',
params: { user: 'jane' },
},
],
},
},
],
};

デフォルトでは、React NavigationはURLを解析するときに、パスセグメントをルート名として使用します。しかし、パスセグメントをルート名に直接変換することは、期待される動作ではない可能性があります。

たとえば、パス/feed/latestを次のように解析したい場合があります。

const state = {
routes: [
{
name: 'Chat',
params: {
sort: 'latest',
},
},
];
}

ニーズに合わせてディープリンクの解析方法を制御するために、linkingconfigオプションを指定できます。

const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};

ここで、ChatはURL/feedを処理する画面の名前であり、ProfileはURL/userを処理します。

configオプションは、コンテナのlinkingプロップで渡すことができます。

import { NavigationContainer } from '@react-navigation/native';

const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};

const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
config,
};

function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
{/* content */}
</NavigationContainer>
);
}

configオブジェクトは、アプリのナビゲーション構造と一致する必要があります。たとえば、上記の構成は、ルートのナビゲーターにChatProfile画面がある場合です。

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}

Chat画面がネストされたナビゲーター内にある場合、それを考慮する必要があります。たとえば、Profile画面がルートにあり、Chat画面がHome内にネストされている次の構造を考えてみます。

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}

function HomeScreen() {
return (
<Tab.Navigator>
<Tab.Screen name="Chat" component={ChatScreen} />
</Tab.Navigator>
);
}

上記の構造の場合、構成は次のようになります。

const config = {
screens: {
Home: {
screens: {
Chat: 'feed/:sort',
},
},
Profile: 'user',
},
};

同様に、ネストは構成に反映される必要があります。詳細については、ネストされたナビゲーターの処理を参照してください。

パラメーターの受け渡し

一般的なユースケースは、データを渡すために画面にパラメーターを渡すことです。たとえば、Profile画面にidパラメーターを持たせて、どのユーザーのプロファイルであるかを知りたい場合があります。ディープリンクを処理するときに、URLを介して画面にパラメーターを渡すことができます。

デフォルトでは、クエリパラメーターが解析されて、画面のパラメーターを取得します。たとえば、上記の例では、URL/user?id=wojciechidパラメーターをProfile画面に渡します。

URLからパラメーターを解析する方法をカスタマイズすることもできます。クエリパラメーターにidを含めるのではなく、idパラメーターがwojciechであるURLを/user/wojciechのようにしたいとします。これを行うには、pathuser/:idを指定します。**パスセグメントが:で始まる場合、パラメーターとして扱われます。**たとえば、URL/user/wojciechは、idパラメーターの値として文字列wojciechを持つProfile画面に解決され、Profile画面のroute.params.idで使用できます。

デフォルトでは、すべてのパラメーターは文字列として扱われます。パラメーターを解析するための関数をparseプロパティに、文字列に変換するための関数をstringifyプロパティに指定することで、解析方法をカスタマイズすることもできます。

/user/wojciech/settingsを解決して、パラメーター{ id: 'user-wojciech' section: 'settings' }にする場合、Profileの構成を次のようにすることができます。

const config = {
screens: {
Profile: {
path: 'user/:id/:section',
parse: {
id: (id) => `user-${id}`,
},
stringify: {
id: (id) => id.replace(/^user-/, ''),
},
},
},
};

これは次のような結果になります。

const state = {
routes: [
{
name: 'Profile',
params: { id: 'user-wojciech', section: 'settings' },
},
],
};

パラメーターをオプションとしてマークする

特定の条件によっては、URLにパラメーターが存在しない場合があります。たとえば、上記のシナリオでは、URLにsectionパラメーターがない場合もあります。つまり、/user/wojciech/settings/user/wojciechの両方がProfile画面に移動しますが、sectionパラメーター(この場合は値がsettings)が存在する場合と存在しない場合があります。

この場合、sectionパラメーターをオプションとしてマークする必要があります。パラメーター名の後に?サフィックスを追加して行うことができます。

const config = {
screens: {
Profile: {
path: 'user/:id/:section?',
parse: {
id: (id) => `user-${id}`,
},
stringify: {
id: (id) => id.replace(/^user-/, ''),
},
},
},
};

URL/users/wojciechを使用すると、次のようになります。

const state = {
routes: [
{
name: 'Profile',
params: { id: 'user-wojciech' },
},
],
};

URLにsectionパラメーターが含まれている場合(例:/users/wojciech/settings)、同じ構成で次のようになります。

const state = {
routes: [
{
name: 'Profile',
params: { id: 'user-wojciech', section: 'settings' },
},
],
};

ネストされたナビゲーターの処理

ターゲットナビゲーターがディープリンクの一部ではない他のナビゲーターにネストされている場合があります。たとえば、ナビゲーション構造が次のようになっているとします。

function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Profile" component={Profile} />
<Tab.Screen name="Feed" component={Feed} />
</Tab.Navigator>
);
}

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
);
}

ルートにスタックナビゲーターがあり、ルートスタックのHome画面内に、さまざまな画面を持つタブナビゲーターがあります。この構造では、パス/users/:idProfile画面に移動したいとします。ネストされた構成は次のように表現できます。

const config = {
screens: {
Home: {
screens: {
Profile: 'users/:id',
},
},
},
};

この構成では、Profile画面がusers/:idパターンに対して解決されることを指定し、Home画面内にネストされています。その後、users/janeを解析すると、次の状態オブジェクトになります。

const state = {
routes: [
{
name: 'Home',
state: {
routes: [
{
name: 'Profile',
params: { id: 'jane' },
},
],
},
},
],
};

状態オブジェクトは、ネストされたナビゲーターの階層と一致する必要があることに注意してください。そうでないと、状態は破棄されます。

一致しないルートまたは404の処理

無効なURLでアプリが開かれた場合、ほとんどの場合、何らかの情報を含むエラーページを表示したいでしょう。Webでは、これは一般的に404エラー、つまりページが見つからないエラーとして知られています。

これを処理するには、パスと一致しない他のルートがレンダリングされるキャッチオールルートを定義する必要があります。パスの一致パターンに*を指定して行うことができます。

例えば

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: 'settings',
},
},
NotFound: '*',
},
};

ここでは、NotFoundという名前のルートを定義し、*(つまりすべて)に一致するように設定しています。パスがuser/:idまたはsettingsに一致しない場合、このルートによって一致するようになります。

そのため、/library/settings/notificationのようなパスは、次の状態オブジェクトに解決されます。

const state = {
routes: [{ name: 'NotFound' }],
};

さらに具体的な指定も可能です。たとえば、/settingsの下にある無効なパスに対して異なる画面を表示したい場合は、Settingsの下にそのようなパターンを指定できます。

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: {
path: 'settings',
screens: {
InvalidSettings: '*',
},
},
},
},
NotFound: '*',
},
};

この構成では、パス/settings/notificationは次の状態オブジェクトに解決されます。

const state = {
routes: [
{
name: 'Home',
state: {
index: 1,
routes: [
{ name: 'Feed' },
{
name: 'Settings',
state: {
routes: [
{ name: 'InvalidSettings', path: '/settings/notification' },
],
},
},
],
},
},
],
};

NotFound画面に渡されるrouteには、ページを開いたパスに対応するpathプロパティが含まれています。必要に応じて、このプロパティを使用して画面に表示される内容をカスタマイズできます(例:WebViewでページを読み込む)。

function NotFoundScreen({ route }) {
if (route.path) {
return <WebView source={{ uri: `https://mywebsite.com/${route.path}` }} />;
}

return <Text>This screen doesn't exist!</Text>;
}

サーバーサイドレンダリングを行う場合も、404エラーに対して正しいステータスコードを返す必要があります。処理方法については、サーバーサイドレンダリングのドキュメントを参照してください。

初期ルートのレンダリング

特定の画面を常にナビゲーターの状態の最初の画面として表示したい場合があります。initialRouteNameプロパティを使用して、最初の画面に使用する画面を指定できます。

上記の例では、Homeの下のナビゲーターでFeed画面を最初のルートにしたい場合、構成は次のようになります。

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: 'settings',
},
},
},
};

すると、パス/users/42は次の状態オブジェクトに解決されます。

const state = {
routes: [
{
name: 'Home',
state: {
index: 1,
routes: [
{ name: 'Feed' },
{
name: 'Profile',
params: { id: '42' },
},
],
},
},
],
};
警告

initialRouteNameは、React Navigationの状態に画面を追加するだけです。アプリがWeb上で実行されている場合、ユーザーがまだアクセスしていないため、ブラウザーの履歴にはこの画面は含まれません。そのため、ユーザーがブラウザーの戻るボタンを押しても、この画面に戻りません。

もう1つ考慮すべき点は、URLを介して初期画面にパラメーターを渡すことができないということです。そのため、初期ルートにパラメーターが必要ないか、必要なパラメーターを渡すために画面構成でinitialParamsを指定してください。

この場合、URLのパラメーターは、パスパターンusers/:idに一致するProfile画面にのみ渡され、Feed画面にはパラメーターは渡されません。Feed画面にも同じパラメーターを含めたい場合は、カスタムgetStateFromPath関数を指定して、それらのパラメーターをコピーできます。

同様に、子画面から親画面のパラメーターにアクセスしたい場合は、React Contextを使用して公開できます。

完全一致パスのマッチング

デフォルトでは、各画面に定義されたパスは、親画面のパスを基準としたURLに対して照合されます。次の構成を考えてみましょう。

const config = {
screens: {
Home: {
path: 'feed',
screens: {
Profile: 'users/:id',
},
},
},
};

ここでは、Home画面と子画面であるProfile画面の両方にpathプロパティが定義されています。プロファイル画面はパスusers/:idを指定していますが、パスfeedを持つ画面の中にネストされているため、パターンfeed/users/:idに一致しようとします。

これにより、URL/feedHome画面に、/feed/users/calProfile画面に移動します。

この場合、/feed/users/calではなく、/users/calのようなURLを使用してProfile画面に移動する方が理にかなっています。これを実現するには、相対マッチングの動作をexactマッチングにオーバーライドできます。

const config = {
screens: {
Home: {
path: 'feed',
screens: {
Profile: {
path: 'users/:id',
exact: true,
},
},
},
},
};

exactプロパティをtrueに設定すると、Profileは親画面のpath構成を無視し、users/calのようなURLを使用してProfileに移動できます。

パスからの画面の省略

場合によっては、画面のルート名をパスに含めたくない場合があります。たとえば、Home画面があり、ナビゲーション状態が次のようになっているとします。

const state = {
routes: [{ name: 'Home' }],
};

この状態を次の構成でパスにシリアル化すると、/homeになります。

const config = {
screens: {
Home: {
path: 'home',
screens: {
Profile: 'users/:id',
},
},
},
};

しかし、ホーム画面にアクセスする際にURLが/だけであれば、より良いでしょう。パスとして空の文字列を指定するか、パスをまったく指定しないと、React Navigationはパスに画面を追加しません(パスに空の文字列を追加することと同じで、何も変更されません)。

const config = {
screens: {
Home: {
path: '',
screens: {
Profile: 'users/:id',
},
},
},
};

パラメーターのシリアル化と解析

URLは文字列であるため、ルートのパラメーターもパスを作成する際に文字列に変換されます。

たとえば、次のような状態があるとします。

const state = {
routes: [
{
name: 'Chat',
params: { at: 1589842744264 },
},
];
}

次の構成では、chat/1589842744264に変換されます。

const config = {
screens: {
Chat: 'chat/:date',
},
};

このパスを解析すると、次の状態が得られます。

const state = {
routes: [
{
name: 'Chat',
params: { date: '1589842744264' },
},
];
}

ここで、dateパラメーターはタイムスタンプであるため数値のはずですが、React Navigationはそれを認識しないため、文字列として解析されました。解析に使用するカスタム関数を提供することで、これをカスタマイズできます。

const config = {
screens: {
Chat: {
path: 'chat/:date',
parse: {
date: Number,
},
},
},
};

パラメーターをシリアル化するカスタム関数も提供できます。たとえば、タイムスタンプの代わりにパスでDD-MM-YYYY形式を使用したいとします。

const config = {
screens: {
Chat: {
path: 'chat/:date',
parse: {
date: (date) => new Date(date).getTime(),
},
stringify: {
date: (date) => {
const d = new Date(date);

return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate();
},
},
},
},
};

要件に応じて、この機能を使用して、より複雑なデータの解析と文字列化を行うことができます。

高度なケース

高度なケースによっては、マッピングを指定するだけでは不十分な場合があります。このようなケースを処理するには、URLを状態オブジェクトに解析するカスタム関数(getStateFromPath)と、状態オブジェクトをURLにシリアル化するカスタム関数(getPathFromState)を指定できます。

const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
config: {
screens: {
Chat: 'feed/:sort',
},
},
getStateFromPath: (path, options) => {
// Return a state object here
// You can also reuse the default logic by importing `getStateFromPath` from `@react-navigation/native`
},
getPathFromState(state, config) {
// Return a path string here
// You can also reuse the default logic by importing `getPathFromState` from `@react-navigation/native`
},
};

構成の更新

以前のバージョンのReact Navigationでは、リンキングの構成形式が少し異なっていました。古い構成では、ネストされたナビゲーターに関係なく、オブジェクトに単純なキーと値のペアを許可していました。

const config = {
Home: 'home',
Feed: 'feed',
Profile: 'profile',
Settings: 'settings',
};

たとえば、Feed画面とProfile画面がHomeの中にネストされているとします。上記の構成ではそのようなネストがなくても、URLが/home/profileであれば機能します。さらに、パスセグメントとルート名を同じように扱うため、構成に指定されていない画面にディープリンクすることもできます。たとえば、Homeの中にAlbums画面がある場合、ディープリンク/home/Albumsはその画面に移動します。これは場合によっては望ましいかもしれませんが、特定の画面へのアクセスを防止する方法はありません。このアプローチでは、任意のルート名が有効なパスであるため、404画面のようなものを持つことも不可能です。

最新バージョンのReact Navigationでは、これとは異なる、より厳格な構成形式が使用されます。

  • 構成の形状は、ナビゲーション構造のネストの形状と一致する必要があります。
  • 構成で定義されている画面のみが、ディープリンクの対象となります。

そのため、上記の構成を次の形式にリファクタリングする必要があります。

const config = {
screens: {
Home: {
path: 'home',
screens: {
Feed: 'feed',
Profile: 'profile',
},
},
Settings: 'settings',
},
};

ここでは、構成オブジェクトに新しいscreensプロパティが追加され、ナビゲーション構造に合わせてFeedProfileの構成がHomeの下にネストされています。

古い形式を使用している場合でも、変更せずに機能し続けます。ただし、一致しない画面を処理するためのワイルドカードパターンを指定したり、画面をディープリンクから防ぐことはできません。古い形式は次のメジャーリリースで削除される予定です。そのため、可能な限り新しい形式に移行することをお勧めします。

プレイグラウンド

以下の構成とパスをカスタマイズして、パスがどのように解析されるかを確認できます。

ホーム
フィード
プロフィール
ID:"vergil"
設定

サンプルアプリ

サンプルアプリでは、Expoのマネージドワークフローを使用します。このガイドでは、コンポーネントの作成ではなく、ディープリンキングの構成の作成に焦点を当てますが、githubリポジトリで完全な実装を確認できます。

最初に、アプリのナビゲーション構造を決定する必要があります。単純にするために、メインナビゲーターは、2つの画面を持つボトムタブナビゲーターになります。最初の画面は、HomeProfileという2つの画面を持つシンプルなスタックナビゲーターであるHomeStackであり、2番目のタブ画面は、ネストされたナビゲーターのない単純な画面であるSettingsです。

BottomTabs
├── Stack (HomeStack)
│   ├── Home
│   └── Profile
└── Settings

ナビゲーション構造を作成した後、各画面をパスセグメントにマッピングするディープリンキングの構成を作成できます。たとえば

const config = {
screens: {
HomeStack: {
screens: {
Home: 'home',
Profile: 'user',
},
},
Settings: 'settings',
},
};

ご覧のように、HomeProfileHomeStackscreensプロパティにネストされています。つまり、/home URLを渡すと、HomeStack->Home状態オブジェクトに解決されます(同様に、/userHomeStack->Profileになります)。このオブジェクトのネストは、ナビゲーターのネストと一致する必要があります。

ここで、HomeStackプロパティには構成オブジェクトが含まれています。構成は必要に応じて深くすることができます。たとえば、Homeがナビゲーターの場合、screensプロパティを持つオブジェクトを作成し、さらに画面やナビゲーターを内部に配置して、URL文字列を読みやすくすることができます。

ナビゲーターの最初の画面として特定の画面を使用したい場合を考えましょう。例えば、Home画面を開くURLがあり、そこからナビゲーションのnavigation.goBack()メソッドを使用してProfile画面に移動したいとします。これは、ナビゲーターのinitialRouteNameを定義することで可能です。以下のように記述します。

const config = {
screens: {
HomeStack: {
initialRouteName: 'Profile',
screens: {
Home: 'home',
Profile: 'user',
},
},
Settings: 'settings',
},
};