mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-12 22:51:18 +08:00
feat: add helper to get focused route name from nested state (#8435)
Currently, to access the focused child screen, we need to do something like this: ```js const routeName = route.state ? route.state.routes[route.state.index].name : route.params?.screen || 'Feed'; ``` However, it doesn't handle some cases, such as when `route.state` is partial. This helper will make it easier: ```js const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed'; ```
This commit is contained in:
@@ -2,6 +2,11 @@ import * as React from 'react';
|
||||
import { View, ScrollView, StyleSheet, Platform } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import {
|
||||
getFocusedRouteNameFromRoute,
|
||||
ParamListBase,
|
||||
} from '@react-navigation/native';
|
||||
import { StackScreenProps } from '@react-navigation/stack';
|
||||
import {
|
||||
createBottomTabNavigator,
|
||||
BottomTabNavigationProp,
|
||||
@@ -59,7 +64,18 @@ const AlbumsScreen = ({
|
||||
|
||||
const BottomTabs = createBottomTabNavigator<BottomTabParams>();
|
||||
|
||||
export default function BottomTabsScreen() {
|
||||
export default function BottomTabsScreen({
|
||||
navigation,
|
||||
route,
|
||||
}: StackScreenProps<ParamListBase, string>) {
|
||||
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Article';
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: routeName,
|
||||
});
|
||||
}, [navigation, routeName]);
|
||||
|
||||
return (
|
||||
<BottomTabs.Navigator
|
||||
screenOptions={{
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import getFocusedRouteNameFromRoute from '../getFocusedRouteNameFromRoute';
|
||||
|
||||
it('gets undefined if there is no nested state', () => {
|
||||
expect(getFocusedRouteNameFromRoute({ name: 'Home' })).toBe(undefined);
|
||||
});
|
||||
|
||||
it('gets focused route name from nested state', () => {
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
state: {
|
||||
routes: [{ name: 'Article' }],
|
||||
},
|
||||
})
|
||||
).toBe('Article');
|
||||
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
state: {
|
||||
index: 1,
|
||||
routes: [{ name: 'Article' }, { name: 'Chat' }, { name: 'Album' }],
|
||||
},
|
||||
})
|
||||
).toBe('Chat');
|
||||
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
state: {
|
||||
routes: [{ name: 'Article' }, { name: 'Chat' }],
|
||||
},
|
||||
})
|
||||
).toBe('Chat');
|
||||
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
state: {
|
||||
type: 'tab',
|
||||
routes: [{ name: 'Article' }, { name: 'Chat' }],
|
||||
},
|
||||
})
|
||||
).toBe('Article');
|
||||
});
|
||||
|
||||
it('gets nested screen in params if present', () => {
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
params: { screen: 'Chat' },
|
||||
})
|
||||
).toBe('Chat');
|
||||
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
params: { screen: 'Chat', initial: false },
|
||||
})
|
||||
).toBe('Chat');
|
||||
|
||||
expect(
|
||||
getFocusedRouteNameFromRoute({
|
||||
name: 'Home',
|
||||
params: { screen: {} },
|
||||
})
|
||||
).toBe(undefined);
|
||||
});
|
||||
29
packages/core/src/getFocusedRouteNameFromRoute.tsx
Normal file
29
packages/core/src/getFocusedRouteNameFromRoute.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
Route,
|
||||
PartialState,
|
||||
NavigationState,
|
||||
} from '@react-navigation/routers';
|
||||
|
||||
export default function getFocusedRouteNameFromRoute(
|
||||
route: Partial<Route<string>> & { state?: PartialState<NavigationState> }
|
||||
): string | undefined {
|
||||
const state = route.state;
|
||||
const params = route.params as { screen?: unknown } | undefined;
|
||||
|
||||
const routeName = state
|
||||
? // Get the currently active route name in the nested navigator
|
||||
state.routes[
|
||||
// If we have a partial state without index, for tab/drawer, first screen will be focused one, and last for stack
|
||||
// The type property will only exist for rehydrated state and not for state from deep link
|
||||
state.index ??
|
||||
(typeof state.type === 'string' && state.type !== 'stack'
|
||||
? 0
|
||||
: state.routes.length - 1)
|
||||
].name
|
||||
: // If state doesn't exist, we need to default to `screen` param if available
|
||||
typeof params?.screen === 'string'
|
||||
? params.screen
|
||||
: undefined;
|
||||
|
||||
return routeName;
|
||||
}
|
||||
@@ -20,4 +20,6 @@ export { default as getStateFromPath } from './getStateFromPath';
|
||||
export { default as getPathFromState } from './getPathFromState';
|
||||
export { default as getActionFromState } from './getActionFromState';
|
||||
|
||||
export { default as getFocusedRouteNameFromRoute } from './getFocusedRouteNameFromRoute';
|
||||
|
||||
export * from './types';
|
||||
|
||||
Reference in New Issue
Block a user