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:
Satyajit Sahoo
2020-06-15 13:52:38 +02:00
committed by GitHub
parent e000138fe5
commit f51f9c8493
4 changed files with 116 additions and 1 deletions

View File

@@ -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={{

View File

@@ -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);
});

View 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;
}

View File

@@ -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';