react navigation, get name of nested route - react-native

I have a nested drawer navigator below, I am using a custom component in the header:
header: props => {
return <DrawerHeader {...props} />;
},
When I try and access from props the current route in my header, like below, the title is undefined, how can I get the current route?
render() {
const {
navigation,
videos,
search: {term},
scene: {
route: {routeName: title}, // undefined
},
} = this.props;
return (
<View>
<View style={styles.container}>
Navigator:
function DrawerStack() {
return (
<Drawer.Navigator>
<Drawer.Screen
name="VideoEpisodesScreen"
component={VideoEpisodesScreen}
/>
<Drawer.Screen name="TestYourselfScreen" component={TestYourselfScreen} />
<Drawer.Screen name="MyResultsScreen" component={MyResultsScreen} />
<Drawer.Screen name="AboutScreen" component={AboutScreen} />
<Drawer.Screen name="TestsScreen" component={TestsScreen} />
<Drawer.Screen
name="BookmarkedVideosScreen"
component={BookmarkedVideosScreen}
/>
</Drawer.Navigator>
);
}
export default function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={stackOptions}
/>
<Stack.Screen
name="Drawer"
component={DrawerStack}
options={drawerOptions}
/>
<Stack.Screen
name="MyResultsScreen"
component={MyResultsScreen}
options={options}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

Funnily enough I had the exact same problem and I found your question after it was just an hour old. Essentially the problem is that React Navigation will only give you the current route of the navigator containing the header. If you have a nested navigator, you won't be able to get it.
It looks like this is somewhat intentional, but I've found that by manually querying the state of the navigator, you can drill down to the "deepest" navigator route. Note that while this works for react-navigation 5, it may not work in the future.
You can iteratively query the nested state like this:
const state = navigation.dangerouslyGetState();
let actualRoute = state.routes[state.index];
while (actualRoute.state) {
actualRoute = actualRoute.state.routes[actualRoute.state.index];
}
Note that this is extremely brittle, but it seems to work good enough for my use cases. You should consider creating an issue/feature request on the react-navigation repository for supporting this use case officially.

React Navigation v6 solution:
A nested route object can be discovered through parent Screen listeners. It's given in a callback argument, and can be passed to getFocusedRouteNameFromRoute to get the route name. In the example shown below, I chose to utilize it during the event 'state' (whenever state changes in the nested stack), but you can use it elsewhere if you want.
<Screen
listeners={({ route }) => ({
state: () => {
const subRoute = getFocusedRouteNameFromRoute(route)
// Your logic here //
}
})}
/>

I think in react-navigation 5, you can access route from this.props
const { route } = this.props;

Related

cleanest way to trigger a function in child component from parent component in react native

I followed a react native tutorial that builds a todo list(and stores todo items with useState) and I wanted to add delete-todo item functionality. I put a delete button(using Icon react native elements) on the navbar(in App(App.js) component's return):
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="home"
component={HomeScreen}
options={({ navigation }) => ({
headerRight: () => (
<View>
<Icon
...
onPress={() => HomeScreen.deleteSelected()}
/>
</View>
),
})}
/>
...
</Stack.Navigator>
</NavigationContainer>
However, this doesn't work since deleteSelected is a local function defined in HomeScreen(HomeScreen.js) component:
export default function HomeScreen({ navigation }) {
...
const [items, setItems] = useState([]);
const [selectedItems, setSelectedItems] = useState([]);
const deleteSelected = () => {...}
I defined deleteSelected inside the HomeScreen component since the required states(items and selectedItems) are defined there.
After some research, I found I can implement such a trigger using forwardRef and useImperativeHandle as shown in https://stackoverflow.com/a/65389631/20883133 . I am wondering if there is a cleaner(more versatile) way to implement a function trigger from the parent component to a function in the child component in react native.
I was told some state manager dependencies can help, like Zustand or Recoil. I read recoil docs in detail but couldn't figure out how it can help with such a problem.

React Native Navigation: Another Navigator is already registered for this container

I want my component to render a TopTab Navigator on the top and also a Drawer Navigator at the same time.
So something like
<TopTab.Navigator>
<TopTab.Screen />
</TopTab.Navigator>
<Drawer.Navigator>
<Drawer.Screen />
</Drawer.Navigator>
However I'm getting an error of "Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen" Make sure each navigator is under a separate "ScreenContainer"
Why dont you try using it like this, drawerNavigator holds as the main wrapper and inside it topTab
const HomeScreen = () => {
return(
<TopTab.Navigator>
<TopTab.Screen />
</TopTab.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
</NavigationContainer>
)
}
This should work, feel free for doubts
You have to set Tab navigator inside drawer navigator, you can search to get better solutions like "how we use multiple navigator in react native?"
visit below link
https://dev.to/easybuoy/combining-stack-tab-drawer-navigations-in-react-native-with-react-navigation-5-da

How to navigate from Home screen to Login screen in a nested navigation ? React Navigation v6

So I'm trying to improve my navigation in my React Native project using React Navigation. I would like to know how to navigate through a nested navigator from Home to Login screen.
navigation.ts
export type RootStackParamList = {
AuthorizedTabStack: BottomTabScreenProps<AuthorizedTabNavigationList>;
AuthorizedModalStack: NavigatorScreenParams<AuthorizedModalList>;
UnauthorizedStack: NavigatorScreenParams<UnauthorizedStackList>;
};
export type AuthorizedTabNavigationList = {
Home: undefined;
Planner: undefined;
};
export type AuthorizedModalList = {
InputModal: undefined;
};
export type UnauthorizedStackList = {
Login: undefined;
};
In my MainNavigator.tsx, I've implemented this...
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
{auth.currentUser ? (
<>
<Stack.Screen name="AuthorizedTabStack" component={TabNavigation} />
<Stack.Screen
name="AuthorizedModalStack"
component={ModalNavigation}
/>
</>
) : (
<Stack.Screen
name="UnauthorizedStack"
component={UnauthorizedStack}
/>
)}
</Stack.Navigator>
</NavigationContainer>
The UnauthorizedStackList is basically a StackNavigator
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Navigator>
I tried using this and there is an error: The action 'NAVIGATE' with payload {"name":"UnauthorizedStack","params":{"screen":"Login"}} was not handled by any navigator.
Do you have a screen named 'UnauthorizedStack'?
const navigation = useNavigation<NavigationProps>();
const handlePressLogOut = () => {
logOut()
navigation.navigate('UnauthorizedStack', { screen: 'Login' });
};
Please let me know if there are better practices on nested navigator or anything else in the code as well. I would like to learn more!
Your navigation.navigate(...) is called before auth state updates, because changing state is not happening right away. You cannot navigate to a screen, that is not rendered.
A few notes about your code:
you don't have to call navigation.navigate(...) after logging out, because you are conditionally rendering screens with auth.currentUser, so UnauthorizedStack will be rendered right after logOut(),
if Login screen is the only screen in your stack, probably there is no need for using stack,

How to achieve 'goBack' in React-Navigation/Drawer 6

I'm having problem in implementing 'goBack' function in React-navigation/drawer 6 ("#react-navigation/drawer": "^6.1.4", precisely).
I was able to perfectly achieve it in react-navigation/drawer 5 with the following codes:
<Drawer.Navigator>
<Drawer.Screen
....
options={{
header: ({ scene }) => {
const { options } = scene.descriptor;
const title = options.headerTitle;
return (
<MyHeader
title={title}
iconName={"menu"}
goback={
()=>scene.descriptor.navigation.goBack()}
/>
);
},
}}
/>
</Drawer.Navigator>
The same revised codes for react-navigation/drawer 6 (as shown below) will take me back to the initial screen (and not on previous screen). It will also give a warning and error message.
<Drawer.Navigator>
<Drawer.Screen
....
options={{
header: ({ navigation, route, options }) => {
const title = getHeaderTitle(options, route.name);
return (
<MyHeader
title={title}
iconName={"menu"}
goback={
()=>navigation.goBack()}
/>
);
},
}}
/>
</Drawer.Navigator>
Please, how can I achieve this 'goBack' in react-navigation/drawer 6?
You need to specify backBehavior
<Drawer.Navigator backBehavior="history">
Please read the upgrade guide when upgrading which documents these changes: https://reactnavigation.org/docs/upgrading-from-5.x/#the-default-value-for-backbehavior-is-now-firstroute-for-tabs-and-drawer
I've faced the same issue and tried to fix it in the way #satya164 provided but then I realised that I just had wrong navigation structure.
If you want to go back from screen B to screen A, they should probably be nested in one Stack rather than in Drawer.
In my case at least it was a better solution.

How can I pass parameters to another tab navigation screen

Hi guys I'm still new to react native, I wanted to ask how can I pass a parameter to another screen? I'm currently doing a login system which going to access the email of the user and show their email on their profile page after they logged in. I've tried { this.props.navigation.state.params.Email } in the profile.js but it shows an error says that
TypeError: undefined is not an object (evaluating 'this.props.navigation.state.params')
I saw some related solutions as well but those were for stack navigator but I'm using bottom tab navigation so I wasn't sure if they work in the same way.
login.js
this.props.navigation.navigate('Profile', { Email: email });
app.js
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={getTransaction} />
<Tab.Screen name="addTransaction" component={addTransaction} />
<Tab.Screen name="Register" component={register} />
<Tab.Screen name="Login" component={login} />
<Tab.Screen name="Profile" component={profile} />
</Tab.Navigator>
</NavigationContainer>
);
}
in your Profile screen page:
export const profile = ({route}) => {
const { Email } = route.params;
}
Then you can use 'Email' as a regular constant.
https://reactnavigation.org/docs/params/
I believe you should get the params from props.router instead of props.navigation.