ネストされたナビゲーターを持つ画面オプション
このドキュメントでは、複数のナビゲーターがある場合の画面オプションの動作について説明します。 `options`を正しい場所に配置し、ナビゲーターを適切に設定するには、この理解が重要です。間違った場所に配置すると、最悪の場合、混乱したり予期しない動作が発生します。
ナビゲーターのナビゲーションオプションは、そのナビゲーターのいずれかの画面コンポーネントからのみ変更できます。これは、画面としてネストされたナビゲーターにも同様に適用されます。
たとえば、各タブにネイティブスタックを含むタブナビゲーターを考えてみましょう。スタック内の画面で`options`を設定するとどうなるでしょうか?
const Tab = createTabNavigator();
const HomeStack = createNativeStackNavigator();
const SettingsStack = createNativeStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen
name="A"
component={A}
options={{ tabBarLabel: 'Home!' }}
/>
</HomeStack.Navigator>
);
}
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="B"
component={B}
options={{ tabBarLabel: 'Settings!' }}
/>
</SettingsStack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
前述のように、ナビゲーターのナビゲーションオプションは、そのナビゲーターのいずれかの画面コンポーネントからのみ変更できます。上記の`A`と`B`は、タブナビゲーターではなく、`HomeStack`と`SettingsStack`内の画面コンポーネントです。そのため、`tabBarLabel`プロパティはタブナビゲーターに適用されません。しかし、これを修正できます!
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{ tabBarLabel: 'Home!' }}
/>
<Tab.Screen
name="Settings"
component={SettingsStackScreen}
options={{ tabBarLabel: 'Settings!' }}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
`HomeStack`と`SettingsStack`コンポーネントを含む`Screen`コンポーネントで直接`options`を設定すると、画面コンポーネントとして使用される場合に、親ナビゲーターのオプションを制御できます。この場合、スタックコンポーネントのオプションによって、スタックをレンダリングするタブナビゲーターのラベルが設定されます。
子ナビゲーターの状態に基づく親画面オプションの設定
次の設定を考えてみましょう。
const Tab = createBottomTabNavigator();
function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Account" component={AccountScreen} />
</Tab.Navigator>
);
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeTabs} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
`FeedScreen`の`options`で`headerTitle`を設定しても、機能しません。これは、`App`スタックが設定のためにその直下の子供(`HomeTabs`と`SettingsScreen`)しか見ないためです。
しかし、`getFocusedRouteNameFromRoute`ヘルパーを使用して、タブナビゲーターのナビゲーション状態に基づいて`headerTitle`オプションを決定できます。最初にタイトルを取得する関数を作成しましょう。
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
function getHeaderTitle(route) {
// If the focused route is not found, we need to assume it's the initial screen
// This can happen during if there hasn't been any navigation inside the screen
// In our case, it's "Feed" as that's the first screen inside the navigator
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
switch (routeName) {
case 'Feed':
return 'News feed';
case 'Profile':
return 'My profile';
case 'Account':
return 'My account';
}
}
次に、この関数を`Screen`の`options`プロップで使用できます。
<Stack.Screen
name="Home"
component={HomeTabs}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
ここで何が起こっているのでしょうか?`getFocusedRouteNameFromRoute`ヘルパーを使用すると、この子ナビゲーター(この場合はレンダリングしているタブナビゲーター)から現在アクティブなルート名を取得し、ヘッダーに適切なタイトルを設定できます。
このアプローチは、子ナビゲーターの状態に基づいて親ナビゲーターのオプションを設定したい場合にいつでも使用できます。一般的なユースケースには、次のものがあります。
- スタックヘッダーにタブタイトルを表示する:スタックにタブナビゲーターが含まれており、スタックヘッダー(上記の例)にタイトルを設定する場合。
- タブバーなしで画面を表示する:タブナビゲーターにスタックが含まれており、特定の画面でタブバーを非表示にする場合(推奨されません。代わりに特定の画面でのタブバーの非表示を参照してください)。
- 特定の画面でドロワーをロックする:ドロワーにスタックが含まれており、特定の画面でドロワーをロックする場合。
多くの場合、同様の動作はナビゲーターの再編成によって実現できます。通常、ユースケースに合致する場合は、このオプションをお勧めします。
たとえば、上記のユースケースでは、スタックナビゲーターの中にタブナビゲーターを追加する代わりに、各タブの中にスタックナビゲーターを追加できます。
const FeedStack = createNativeStackNavigator();
function FeedStackScreen() {
return (
<FeedStack.Navigator>
<FeedStack.Screen name="Feed" component={FeedScreen} />
{/* other screens */}
</FeedStack.Navigator>
);
}
const ProfileStack = createNativeStackNavigator();
function ProfileStackScreen() {
return (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Profile" component={ProfileScreen} />
{/* other screens */}
</ProfileStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
);
}
const RootStack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={HomeTabs} />
<RootStack.Screen name="Settings" component={SettingsScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
}
さらに、これにより、これらのスタックにルートを追加することで、タブバーを非表示にせずに、フィードとプロファイルのスタックに新しい画面をプッシュできます。
タブバーの上に画面をプッシュする場合(つまり、タブバーが表示されない場合)、タブナビゲーター内の画面に追加するのではなく、`App`スタックに追加できます。