React Native call navigator inside child navigator screen - reactjs

I have a main navigator in app.js
import React from 'react';
import LoginScreen from './src/screens/LoginScreen';
import HomeScreen from './src/screens/HomeScreen';
import { Image } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { Constans } from './src/constants/Constant';
import SignupScreen from './src/screens/SignupScreen';
import { EventScreen } from './src/screens/EventScreen';
const navigator = createStackNavigator(
{
Login: {
screen: LoginScreen,
navigationOptions: {
headerShown: false
}
},
SignUp: {
screen: SignupScreen,
navigationOptions: {
headerShown: false
}
},
Home: {
screen: HomeScreen,
navigationOptions: {
headerLeft:()=>false,
headerTitle:()=> (
<Image style={{width:35,height:35}} source={Constans.logoImage}/>
)
}
},
Event:{
screen:EventScreen,
navigationOptions: {
headerLeft:()=>false,
headerTitle:()=> (
<Image style={{width:35,height:35}} source={Constans.logoImage}/>
)
}
}
},
{
initialRouteName: 'Home'
}
);
const AppContainer = createAppContainer(navigator);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
and when I go HomeScreen I have also menu navigator.
import React from 'react';
import Icon from 'react-native-vector-icons/FontAwesome';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { TabEventsScreen } from './TabEventsScreen';
import { TabForYouScreen } from './TabForYouScreen';
import { TabProfileScreen } from './TabProfileScreen';
const Tab = createBottomTabNavigator();
export default function HomeScreen() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions=
{
({ route }) =>
(
{
tabBarIcon: ({ focused, color }) =>
{
return <Icon
name={route.name === 'Explore' ? "globe" : route.name === 'Profile' ? 'user' : route.name === 'Wallet' ? 'money' :'star'}
size={focused ? 32: 24}
color={color} />;
}
}
)
}
tabBarOptions=
{
{
activeTintColor: '#1F7A8C',
inactiveTintColor: 'gray',
}
}>
<Tab.Screen name="Explore" component={TabEventsScreen} />
<Tab.Screen name="For You" component={TabForYouScreen} />
<Tab.Screen name="Wallet" component={TabProfileScreen} />
<Tab.Screen name="Profile" component={TabProfileScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
in one tab screen I am trying to navigate Event screen of main navigator. How can I do it?
import React, { useState, useEffect } from 'react';
import { Constans } from './../constants/Constant';
import { View, ImageBackground, Text, ScrollView } from 'react-native';
import { TabEventsScreenStyle } from './../styles/TabEventsScreenStyle';
import { IncomingEvents } from './../services/TabEventsService';
import { EventCard } from './../components/Eventcard'
export function TabEventsScreen({navigation}) {
const [events, setEvents] = useState([]);
const [page, setPage] = useState(1);
_getEvents = () => {
return IncomingEvents(1, page, 20);
}
useEffect(() => {
setEvents(_getEvents().Events);
});
_OnButtonPress=(key)=>{
console.log(navigation)
navigation.navigate('Event',{itemId:key});
}
return (
<ScrollView>
<ImageBackground source={Constans.homeBackGroundImage} style={TabEventsScreenStyle.backgroundImage} >
<Text style={TabEventsScreenStyle.title}>Incoming Events</Text>
<View>
{
events.map(el =>
<Text style={TabEventsScreenStyle.text} onPress={()=> this._OnButtonPress(el.key)}>
<EventCard key={el.key} data={el}></EventCard>
</Text>
)
}
</View>
</ImageBackground>
</ScrollView>
);
}
my error is
The action 'NAVIGATE' with payload
{"name":"Event","params":{"itemId":1}} was not handled by any
navigator.
Do you have a screen named 'Event'?
If you're trying to navigate to a screen in a nested navigator, see
https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
I also tried
const parent_nav=navigation.dangerouslyGetParent();
console.log(navigation);
parent_nav.navigate('Event',{itemId:key});
but parent_nav is undefined
Thanks in advance

You can navigate as if all screens were at the same level.
Navigation actions are handled by current navigator and bubble up if couldn't be handled
For example, if you're calling navigation.goBack() in a nested screen, it'll only go back in the parent navigator if you're already on the first screen of the navigator. Other actions such as navigate work similarly, i.e. navigation will happen in the nested navigator and if the nested navigator couldn't handle it, then the parent navigator will try to handle it. In the above example, when calling navigate('Messages'), inside Feed screen, the nested tab navigator will handle it, but if you call navigate('Settings'), the parent stack navigator will handle it.
The source.
When you navigate, it will try to find your screen in the current navigation and if it failed, then will try with the parent until the root navigation.

Wrap your AppContainer with NavigationContainer instead of the TabNavigator, so that both the StackNavigator and TabNavigator use the same navigation prop
export default class App extends React.Component {
render() {
return(
<NavigationContainer>
<AppContainer />
<NavigationContainer/>
)}
}
And remove NavigationContainer from the TabNavigator
Here is how I nested Stack and Tab Navigator
import React from 'react';
import {Dimensions, StatusBar, StyleSheet} from 'react-native';
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import AScreen from './AScreen';
import BScreen from './BScreen';
import CScreen from './CScreen';
import DScreen from './DScreen';
import A from './a';
import B from './B';
import C from './C';
import D from './D';
const Tab = createMaterialBottomTabNavigator();
const Stack = createStackNavigator();
const BottomTab = () => {
return (
<Tab.Navigator
activeColor="#ff6362"
backBehavior="initialRoute"
barStyle={{backgroundColor: '#000000'}}>
<Tab.Screen
name="A"
component={AScreen}
options={{
tabBarLabel: 'A',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons name="book-open" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="B"
component={BScreen}
options={{
tabBarLabel: 'B',
tabBarIcon: ({color}) => (
<MaterialIcons name="people" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="C"
component={CScreen}
options={{
tabBarLabel: 'C',
tabBarIcon: ({color}) => (
<MaterialIcons name="people" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="D"
component={DScreen}
options={{
tabBarLabel: 'D',
tabBarIcon: ({color}) => (
<MaterialIcons name="person-pin" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
};
export default function AdminScreen(props) {
return (
<NavigationContainer>
<StatusBar hidden />
<Stack.Navigator headerMode="none">
<Stack.Screen name="BottomTab" component={BottomTab} />
<Stack.Screen name="A" component={A} />
<Stack.Screen name="B" component={B} />
<Stack.Screen name="C" component={C} />
<Stack.Screen name="D" component={D} />
</Stack.Navigator>
</NavigationContainer>
);
}

Related

Reactive navigation error: The action 'NAVIGATE' with payload {"name":"Dict"} was not handled by any navigator

I am a beginner of react native. After reading the reactive navigation basics, I'm trying to build my personal app. However, I have a difficulty of navigation to another components.
I only have the following components:
app.js
import { StatusBar } from 'expo-status-bar';
import { Button, StyleSheet, Text, View, TextInput, Image } from 'react-native';
import * as React from 'react';
import { NavigationContainer, StackActions } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import Dict from './dict';
const Stack = createNativeStackNavigator();
const HomeScreen = ({ navigation }) => {
return (
<View style={{ height: '100%' }}>
<View style={{
height: '15%', backgroundColor: 'powderblue', flexDirection:'row'
}} />
<Text>This is a test</Text>
<Button title="Go to Dictionary" onPress={() => navigation.navigate('Dict')}/>
</View>
);
};
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
dict.js
import React from 'react';
import { Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function Dict () {
return (
<Text>Hello, I am your cat!</Text>
);
}
export default Dict;
When I click the button Go to Dictionary, I've received the following error message:
The action 'NAVIGATE' with payload {"name":"Dict"} was not handled by any navigator.
Do you have a screen named 'Dict'?
What I want to achieve is jump to the dictionary page via a button click. Thank you for your help!
You have to create your Dict screen in your Navigator, try this:
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Dict"
component={Dict}
options={{ headerShown: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

How to integrate React Navigation Back Button with a WebView

I tried following this tutorial https://www.reactnativeschool.com/integrating-react-navigation-back-button-with-a-webview.
Here is my Index.js/App.js code. Any guidance mainly HOW TO ADD adding natigationOptions to Stack.Screen.
import React, { useEffect } from "react";
import { StyleSheet, Text, View } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import {
createStackNavigator,
HeaderBackButton, // new import
} from "#react-navigation/stack";
import Home from "./screens/Home";
import Details from "./screens/Details";
import Icon from "react-native-vector-icons/FontAwesome";
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: true,
}}
>
<Stack.Screen name="Home" component={Tabs} />
<Stack.Screen
name="Details"
component={Details}
options={({ navigation }) => ({
headerTitle: "Details",
headerLeft: (props) => {
const headerLeftInfo = navigation.getParam("headerLeftInfo");
if (headerLeftInfo) {
return (
<HeaderBackButton
{...props}
title={headerLeftInfo.title}
onPress={headerLeftInfo.onPress}
/>
);
}
return <HeaderBackButton {...props} />;
},
})}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
const styles = StyleSheet.create({});
How to add navigationOptions like the code below:
const App = createStackNavigator({
Index: {
screen: Index,
navigationOptions: {
headerTitle: 'Index',
},
},
Details: {
screen: Details,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Details',
}),
},
});

How to nest a Stack Navigation inside a Drawer Navigation Component in React Native

I'm trying to nest a Navigation Stack inside a Navigation Drawer. But it is not working. What seems to be the problem ?
App.js (main file)
import { StatusBar } from 'expo-status-bar';
import React, {useState} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import AppLoading from 'expo-app-loading';
import Navigator from './routes/Drawer';
export default function App() {
return (
<Navigator />
)
}
routes/HomeStack.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Home from '../screens/home';
import ReviewDetails from '../screens/reviewDetails';
const Stack = createStackNavigator()
export default Navigator = () => {
return (
<NavigationContainer initialRouteName='Home'>
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen name='Home'component={Home} />
<Stack.Screen name='ReviewDetails' component={ReviewDetails} options={{ title: 'Review Details' }} />
</Stack.Navigator>
</NavigationContainer>
)
}
routes/Drawer.js
import * as React from 'react';
import { Button, View } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import HomeStack from './HomeStack';
import AboutStack from './AboutStack';
const RootDrawerNavigator = createDrawerNavigator();
export default function Drawer() {
return (
<NavigationContainer>
<RootDrawerNavigator.Navigator initialRouteName="Home">
<RootDrawerNavigator.Screen name="Home" component={HomeStack} />
<RootDrawerNavigator.Screen name="About" component={AboutStack} />
</RootDrawerNavigator.Navigator>
</NavigationContainer>
);
}
And I'm getting this Error: "Error: Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably
an error. If this was intentional, pass 'independent={true}' explicitely. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them."
If Navigator is your Drawer, and you want to nest a StackNavigator inside it, then all you have to do is to give the NavigationContainer to your drawer and remove it from the StackNavigator. You don't need to repeat it again, because StackNavigator will already be inside the NavigationContainer.
Something like:
const RootDrawerNavigator = createDrawerNavigator();
export default function Drawer() {
return (
<NavigationContainer>
<RootDrawerNavigator.Navigator initialRouteName="Home">
<RootDrawerNavigator.Screen name="Home" component={HomeStack} /> // HomeStack is your Stack.Navigator
<RootDrawerNavigator.Screen name="About" component={AboutStack} />
</RootDrawerNavigator.Navigator>
</NavigationContainer>
);
}
And
const Stack = createStackNavigator()
export default function HomeStack() {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen name='Home' component={Home} />
<Stack.Screen name='ReviewDetails' component={ReviewDetails} options={{ title: 'Review Details' }} />
</Stack.Navigator>
)
}

Testing: react-native (expo) take snapshot of a Screen component inside of react-navigation

My test attempt currently looks like this:
import React from "react"
import { NavigationContainer } from "#react-navigation/native"
import { createStackNavigator } from "#react-navigation/stack"
import { render, waitFor } from "#testing-library/react-native"
import { Provider } from "react-redux"
import { createStore } from "redux"
import rootReducer from "src/store/reducers/root-reducer"
import ProfileScreen from "src/screens/profile"
const store = createStore(rootReducer)
test("renders correctly", async () => {
const Screen = () => {
// const navigationRef = React.useRef(null) // cause "Invalid hook call. Hooks can only be called inside .."
const Stack = createStackNavigator()
return (
<Provider store={store}>
<NavigationContainer
// ref={navigationRef} // cause "Invalid hook call. Hooks can only be called inside .."
>
<Stack.Navigator>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
)
}
const tree = render(<Screen />)
await waitFor(async () => expect(tree.toJSON()).toMatchSnapshot())
})
But it keeps throwing this:
...
console.error
The above error occurred in the <ForwardRef(NavigationContainer)> component:
in Provider (created by Screen)
in Screen
Consider adding an error boundary to your tree to customize error handling behavior.
Visit <fb link> to learn more about error boundaries
...
I can't find any useful docs/examples on the react-navigation site regrading testing a screen. (I also googled a lot around)
I can try to boil down the code to something like this:
App.json
...
export default function App() {
...
return (
<Provider store={store}>
<Navigation />
</Provider>
)
}
Navigation.js
import React from "react"
import { NavigationContainer, DarkTheme } from "#react-navigation/native"
import { createStackNavigator } from "#react-navigation/stack"
import BottomTabNavigator from "./BottomTabNavigator"
export default function Navigation() {
const navigationRef = React.useRef(null)
return (
<NavigationContainer
theme={DarkTheme}
ref={navigationRef} // https://reactnavigation.org/docs/navigation-container/#ref
>
<RootNavigator />
</NavigationContainer>
)
}
const Stack = createStackNavigator()
function RootNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
...
</Stack.Navigator>
)
}
BottomTabNavigator
import React from "react"
import { Linking, TouchableWithoutFeedback, View } from "react-native"
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs"
import { createStackNavigator } from "#react-navigation/stack"
import { Feather } from "#expo/vector-icons"
import Profile from "src/screens/profile"
import SignIn from "src/screens/sign-in"
const BottomTab = createBottomTabNavigator()
export default function BottomTabNavigator() {
const TabIcon = ({ name, color }) => <Feather name={name} color={color} size={20} />
return (
<BottomTab.Navigator
tabBarOptions={{
activeTintColor: Colors.defaultWhite,
style,
}}
>
<BottomTab.Screen
name="Profile"
component={ProfileNavigator}
options={{
tabBarIcon: ({ color }) => <TabIcon name="user" color={color} />,
title: I18n.t("components.tab_navigator.profile"),
}}
/>
)
}
const ProfileStack = createStackNavigator()
function ProfileNavigator() {
return (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Profile" component={Profile} options={{ headerShown: false }} />
<ProfileStack.Screen name="SignIn" component={SignIn} options={{ headerShown: false }} />
</ProfileStack.Navigator>
)
}

How to Navigate from DRAWER NAVIGATION to BOTTOM TAB BAR NAVIGATION in react native?

this url: https://streamable.com/no6anz, is a video showcasing my app's navigation, I want the ability to navigate from a Drawer screen to a SPECIFIC Tab Screen in my Tab Navigation.
For example: If i pressed let's say the 'Messages' drawer item, i would like to navigate to the My Account tab screen in the tab navigator.
So far my implementation has not been the best:
import React, {Fragment, Component} from 'react';
import {View, StyleSheet} from 'react-native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {createDrawerNavigator} from '#react-navigation/drawer';
import {createStackNavigator} from '#react-navigation/stack';
import Home from '../Views/HomeView';
import Cart from '../Views/CartView';
import MyAccount from '../Views/MyAccountView';
import Sidebar from '../Views/Sidebar';
import HomeIconColor from './CustomSVGIcons/HomeIconColor';
import CartIconColor from './CustomSVGIcons/CartIconColor';
import PersonIconColor from './CustomSVGIcons/PersonIconColor';
import MenuIconColor from './CustomSVGIcons/MenuIconColor';
import Contact from '../Views/screens/Contact';
import Messages from '../Views/screens/Messages';
import {
NavigationContainer,
DrawerActions,
useNavigation,
} from '#react-navigation/native';
import {BottomTabBar} from '#react-navigation/bottom-tabs';
import Login from '../Views/screens/Login';
import {create} from 'react-test-renderer';
import {createSwitchNavigator} from '#react-navigation/compat';
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
const Switch = createSwitchNavigator();
class Navigator extends Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<NavigationContainer>
<MyDrawerNavigation />
</NavigationContainer>
);
}
}
const MyTabs = (props) => {
const navigation = useNavigation();
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={({route}) => ({
tabBarButton: ['Contact', 'Route2ToExclude'].includes(route.name)
? () => {
return null;
}
: undefined,
})}>
<Tab.Screen
name="Home"
component={Home}
/>
<Tab.Screen
name="MyAccount"
component={MyAccount}
/>
<Tab.Screen
name="Cart"
component={Cart}
/>
<Tab.Screen
name="Sidebar"
component={Sidebar}
/>
<Tab.Screen name="Contact" component={Contact} />
</Tab.Navigator>
);
};
const MyDrawerNavigation = () => {
return (
<Drawer.Navigator drawerPosition={'right'}>
<Drawer.Screen
name="Home"
component={MyTabs}
initialParams={{screen: 'Home'}}
/>
<Drawer.Screen
name="My Account"
component={MyTabs}
initialParams={{screen: 'MyAccount'}}
/>
</Drawer.Navigator>
);
};
const styles = StyleSheet.create({
tabNav: {
backgroundColor: '#000000',
},
});
export default Navigator;
You can pass the screen as the initial param and it would redirect to the correct tab.
<Drawer.Screen name="MyTabs" component={'Tab Component Name'} initialParams={{screen:'MyAccount'}}/>
SOLVED!:
After extensively further researching; I have created my own custom Drawer, with the onPress handler.
The onPress handler initiates the action for navigation to the specified screens.
NOTE: you will have to pass in the navigation ref to the top-level navigation container. You will also have to pass this same reference into your custom drawer module (or any module in that concern)
here's my custom drawer component:
import React, {Component} from 'react';
import {Text, View, Button, StyleSheet} from 'react-native';
import {useNavigation, CommonActions} from '#react-navigation/native';
import * as rootNavigator from '../components/rootNavigator';
const CustomDrawer = () => {
return (
<View style={styles.container}>
<Button
title="My Account"
onPress={() => rootNavigator.navigate('MyAccount')}
/>
<Button
title="Contact"
onPress={() => rootNavigator.navigate('Contact')}
/>
<Button title="Cart" onPress={() => rootNavigator.navigate('Cart')} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 30,
},
});
export default CustomDrawer;
here's the code where I return the DrawerNavBar:
const MyDrawerNavigation = () => {
return (
<NavigationContainer ref={navigationRef}>
<Drawer.Navigator
drawerPosition={'right'}
drawerContent={(props) => <CustomDrawer {...props} />}>
<Drawer.Screen name="Tabs" component={MyTabs} />
<Drawer.Screen name="Contact" component={Contact} />
</Drawer.Navigator>
</NavigationContainer>
);
};

Resources