Navigating from children screen to parent's sibling screen in React Navigation - javascript

I have few questions about React Navigation. I want to navigate from child screen to parent's sibling component, but I couldn't find a proper way for that. My navigation in the app looks like this:
Stack (Main)
LoginPage
Desk (DrawerNavigation)
Messages
MyAccount
Here I want to navigate to MyAccount, from Messages (Desk's child screen). How can I achieve that?

I'm using #react-navigation/stack, #react-navigation/drawer to config and useNavigation to redirect screen:
import * as React from "react";
import { Button, Text, View } from "react-native";
import { NavigationContainer, useNavigation } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import { createDrawerNavigator } from "#react-navigation/drawer";
const STYLE = {
flex: 1,
alignItems: "center",
justifyContent: "center",
width: "100%",
};
function LoginPage() {
const navigation = useNavigation();
return (
<View style={STYLE}>
<Text>LoginPage</Text>
<Button
title="Go to DrawerNav"
onPress={() => navigation.navigate("Desk")}
/>
</View>
);
}
function Messages() {
const navigation = useNavigation();
return (
<View style={STYLE}>
<Text>Messages</Text>
<Button
title="Go to MyAccount"
onPress={() => navigation.navigate("MyAccount")}
/>
</View>
);
}
function MyAccount() {
return (
<View style={STYLE}>
<Text>MyAccount</Text>
</View>
);
}
const Drawer = createDrawerNavigator();
const Desk = () => {
return (
<Drawer.Navigator initialRouteName="Messages">
<Drawer.Screen name="Messages" component={Messages} />
</Drawer.Navigator>
);
};
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="LoginPage">
<Stack.Screen name="LoginPage" component={LoginPage} />
<Stack.Screen
name="Desk"
component={Desk}
options={{ headerShown: false }}
/>
<Stack.Screen name="MyAccount" component={MyAccount} />
</Stack.Navigator>
</NavigationContainer>
);
}

Related

How can i modify the header structure of Drawer Navigator, taking into account that each screen will have a different header?

import * as React from "react";
import { SafeAreaView, useWindowDimensions } from "react-native";
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from "#react-navigation/native";
import { RegistrationScreen } from './src/screens/Registration/RegistrationScreen';
import {LoginScreen} from './src/screens/Login/LoginScreen';
import {NewAnuncioScreen} from './src/screens/NewAnuncio/NewAnuncioScreen';
import { FeedScreen } from "./src/screens/Feed/FeedScreen";
import { FontAwesome, AntDesign, FontAwesome5, Entypo} from '#expo/vector-icons';
const Drawer = createDrawerNavigator()
const App = () => {
const dimensions = useWindowDimensions();
return (
<SafeAreaView style={{ flex: 1 }}>
<NavigationContainer>
<Drawer.Navigator initialRouteName="Feed" screenOptions={{
headerStyle: { backgroundColor: "#f2bc1b" , height: 55},
drawerType: dimensions.width >= 821 ? 'permanent' : 'front',
overlayColor: 'transparent',
drawerContent:{CustomDrawerContent}
}}>
<Drawer.Screen name="Feed" component={FeedScreen}/>
<Drawer.Screen name="Registration" component={RegistrationScreen} options={{ headerShown: false }} />
<Drawer.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
{/* <Drawer.Screen name="Criar Anúncio" component={NewAnuncioScreen} /> */}
</Drawer.Navigator>
</NavigationContainer>
</SafeAreaView>
)
}
export default App;
**I want the header´s structures can vary like that:
enter image description here
enter image description here
But i don´t know how to make this.
If someone help me i thanks.**
You should pass the Drawer props to your custom drawer and retrieve the navigation index.
drawerContent = {(props) => <CustomDrawerContent {...props} />}
Then
const CustomDrawerContent = (props) => {
const [index, setIndex] = useState(0)
useEffect(() => {
setIndex(props.state.index);
}, [props])
...
Now according to this index you can change the appearance and the behavior your drawer content.

Error: "Another navigator is already registered for this container. You likely have multiple navigators under a single Navigation Container" [duplicate]

I wrote below code to make multiple side menus but it occurred error
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 "Screen" container.
However I've tried to find the Container for multiple drawers but no luck.
What should I do?
Thanks in advance.
import React from 'react';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import { Ionicons } from '#expo/vector-icons';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationNativeContainer } from '#react-navigation/native';
import { Container, Text, Button } from 'native-base';
import 'react-native-gesture-handler'
function BlankScreen({ navigation }) {
return (
<Text>Blank</Text>
);
}
function HomeScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.navigate('Menu')}>
<Text>Go to Menu</Text>
</Button>
<Button onPress={() => navigation.navigate('Favorit')}>
<Text>Go to Favorit</Text>
</Button>
</Container>
);
}
function MenuScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-reverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
function FavoritScreen({ navigation }) {
return (
<Container style={{ flex: 1, flexDirection: 'column-inverse' }}>
<Button onPress={() => navigation.goBack()}>
<Text>Go back home</Text>
</Button>
</Container>
);
}
const DrawerL = createDrawerNavigator();
const DrawerR = createDrawerNavigator();
export default function App() {
return (
<Container>
<NavigationNativeContainer>
<DrawerL.Navigator initialRouteName="Home" drawerPosition="left">
<DrawerL.Screen name="Home" component={HomeScreen} />
<DrawerL.Screen name="Menu" component={MenuScreen} />
<DrawerL.Screen name="Favorit" component={FavoritScreen} />
</DrawerL.Navigator>
<DrawerR.Navigator initialRouteName="Blank" drawerPosition="right">
<DrawerR.Screen name="Blank" component={BlankScreen} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
</NavigationNativeContainer>
</Container>
);
The way you wrote it won't work, because consider this: you've 2 drawers, one has initial route as "Home", and other as "Blank". Which one should be rendered?
Here, you need to follow the same approach as React Navigation 4, put one drawer inside another:
function HomeStack() {
return (
<Stack.Navigator screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="Favorit" component={FavoritScreen} />
</Stack.Navigator>
)
}
function RightDrawer() {
return (
<DrawerR.Navigator initialRouteName="Home" drawerPosition="right">
<DrawerR.Screen name="Home" component={HomeStack} />
<DrawerR.Screen name="Menu" component={MenuScreen} />
<DrawerR.Screen name="Favorit" component={FavoritScreen} />
</DrawerR.Navigator>
)
}
function LeftDrawer() {
return (
<DrawerL.Navigator initialRouteName="RightDrawer" drawerPosition="left">
<DrawerL.Screen name="RightDrawer" component={RightDrawer} />
</DrawerL.Navigator>
);
}
function App() {
return (
<NavigationNativeContainer>
<LeftDrawer />
</NavigationNativeContainer>
)
}
Since your left drawer won't show the screens that you have in Stack, you will need to provide a custom component for your drawer which lists the screens: https://reactnavigation.org/docs/en/next/drawer-navigator.html#providing-a-custom-drawercontent

Using navigation.openDrawer in Stack Navigator

I am trying to use the navigation prop inside my Stack Navigator to open the drawer when the material icon is clicked. However when I click th button I recieve the error:
Undefined is not an object (evaluating navigation.openDrawer)
I am confused as I have passed the navigation prop into the 'App' function. Where am I going wrong here?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createDrawerNavigator} from '#react-navigation/drawer';
import HomeScreen from './src/screens/HomeScreen';
import SecondScreen from './src/screens/SecondScreen.js';
import {MaterialIcons} from '#expo/vector-icons';
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
const TheDrawer = () => {
return(
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
);
}
const App = ({navigation}) =>{
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"component={TheDrawer}
options={{headerTitle:
<View>
<MaterialIcons
name='menu'
onPress={() => navigation.openDrawer()} size={28}
/>
</View>
}}
/>
<Stack.Screen name="SecondScreen" component={SecondScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;
The navigation prop only exist inside a Navigator, and different Navigators will have different navigation props (the Stack Navigator navigation will NOT have the openDrawer method, for example). I think that what you want to accomplish is this:
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen
name="Home"
component={HomeScreen}
options={({ navigation }) => ({
headerTitle: (
<View>
<MaterialIcons name="menu" onPress={() => navigation.openDrawer()} size={28}/>
</View>
),
})}
/>
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
};
Or, if you want the menu button available on every page header:
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Home"
screenOptions={({ navigation }) => ({
headerTitle: (
<View>
<MaterialIcons name="menu" onPress={() => navigation.openDrawer()} size={28} />
</View>
),
})}
>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="SecondScreen" component={SecondScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
};
Source: https://reactnavigation.org/docs/headers/
If you need to access more pages inside this two (Home, SecondScreen), you can create it like this:
const HomeScreen = () => {
<Stack.Navigator>
<Stack.Screen name="Home1" component={HomeScreen1} />
<Stack.Screen name="Home2" component={HomeScreen2} />
<Stack.Screen name="Home3" component={HomeScreen3} />
</Stack.Navigator>
}
Source: https://reactnavigation.org/docs/nesting-navigators

In React Native Navigation, how do I send props to my screens?

I want to be able to use navigation on a different screen other than just the first one but I am getting an error that this.props does not exist.
I have my App.js file setup like this:
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import Screen2 from './screens/Screen2';
import Screen3 from './screens/Screen3';
import HomeScreen from './screens/HomeScreen';
const Stack = createStackNavigator();
function HomeScreen({ navigation }) {
return (
<View>
<Button
title="Go to Screen2"
onPress={() => {
navigation.navigate('Screen2');
}}
/>
<Button
title="Go to Screen3"
onPress={() => {
navigation.navigate('Screen3');
}}
/>
</View>
);
const App: () => React$Node = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Screen2" component={Screen2} />
<Stack.Screen name="Screen3" component={Screen3} />
</Stack.Navigator>
</NavigationContainer>
);
};
The buttons in app.js work but if I go to Screen2 and click a button that intends to go to another (Screen3 in the example below), props does not exist.
Example Screen2.js would look like this:
const Screen2: () => React$Node = () => {
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3}}
/>
</View>
</>
);
function goToScreen3() {
if(condition){
this.props.navigate.navigate('Screen3');
}}
How do I pass the props so that I can use navigation in my second screen?
For functional component sometimes it's tricky to pass navigation through props as well. So just use withNavigation.
you have to import it and wrap the function with it.
import { withNavigation } from 'react-navigation';
const Screen2 = props => {
const goToScreen3 = () => {
if(condition){
props.navigate.navigate('Screen3');
}}
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3()}
/>
</View>
</>
);
export default withNavigation(Screen2)
In Functional Component there is no this binding so you need to get the props from the function first
check th
const Screen2 = (props) => {
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3}}
/>
</View>
</>
);
function goToScreen3() {
if(condition){
props.navigate.navigate('Screen3');
}
}
}

React native navigation in flatlist

I using the flatlist to display a list of data. I wish to pass the data into another pages, but i have no idea which kind of navigation I shd use.
import Routes from './src/Routes';
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<StatusBar
backgroundColor="#1c313a"
arStyle="light-content"
/>
<Routes/>
</View>
);
}
}
import React, { Component } from 'react';
import {StyleSheet, View, StatusBar,Text} from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import HomeScreen from '../pages/HomeScreen'
import YourActivitiesScreen from '../pages/YourActivitiesScreen'
import YourFavouriteScreen from '../pages/YourFavouriteScreen'
const Drawer = createDrawerNavigator();
function MyDrawer() {
return (
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{ drawerLabel: 'Home' }}
/>
<Drawer.Screen
name="Your Activities"
component={YourActivitiesScreen}
options={{ drawerLabel: 'Your Activities' }}
/>
<Drawer.Screen
name="Your Favourite"
component={YourFavouriteScreen}
options={{ drawerLabel: 'Your Favourite' }}
/>
</Drawer.Navigator>
);
}
export default function SideMenu() {
return (
<NavigationContainer>
<MyDrawer />
</NavigationContainer>
);
}
import React, { Component } from 'react';
import {Router, Stack, Scene} from 'react-native-router-flux';
import Login from './pages/Login';
import Signup from './pages/Signup';
import SideMenu from './components/SideMenu'
export default class Routes extends Component {
render() {
return(
<Router>
<Stack key="root" hideNavBar>
<Scene key="login" component={Login} title="Login" initial/>
<Scene key="signup" component={Signup} title="Signup" />
<Scene key="home" component={SideMenu} title="HomeScreen" />
</Stack>
</Router>
);
}
}
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
AsyncStorage,
TouchableOpacity,
FlatList,
Button,
} from 'react-native';
import DetailsScreen from './Details';
import { createDrawerNavigator, SafeAreaView } from 'react-navigation';
class HomeScreen extends Component{
constructor(props) {
super(props);
this.state = {
activitiesList: [],
}
};
renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
console.log('test')
}}
>
<View style={styles.item}>
<Text style={styles.title}>{item.name}</Text>
</View>
</TouchableOpacity>
);
}
render(){
const listActivities = this.state.activitiesList
return (
<View>
<View style={styles.container}>
<Text style={styles.heading}>UPCOMING ACTIVITIES</Text>
</View>
<View>
<SafeAreaView>
<FlatList
data = {listActivities}
renderItem={({ item }) => this.renderItem(item)}
keyExtractor={item => item.id}
/>
</SafeAreaView>
</View>
</View>
)
}
}
I used the react-native-router-flux at the early part of system, which is login, signup and home. Now home display the flatlist, from the flatlist i have to make a onPress to navigate to another pages, the Actions in router-flux that i used before cannot work. Anyone have idea about it? or another better way navigate to to the details of flatlist?
First off, I'd refactor all the code to use just one kind of navigator (in this case, going for react-navigation). So, we will have your router-flux code combined with your
//Your routes screen
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
//... add missing imports
const Stack = createStackNavigator();
//this will be your old router-flux
const Root = () => {
return (
<Stack.Navigator>
<Stack.Screen name="login" component={Login} title="Login" initial/>
<Stack.Screen name="signup" component={Signup} title="Signup" />
<Stack.Screen name="home" component={SideMenu} title="HomeScreen" />
</Stack.Navigator>
)
}
const Drawer = () => {
return (
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{ drawerLabel: 'Home' }}
/>
<Drawer.Screen
name="Your Activities"
component={YourActivitiesScreen}
options={{ drawerLabel: 'Your Activities' }}
/>
<Drawer.Screen
name="Your Favourite"
component={YourFavouriteScreen}
options={{ drawerLabel: 'Your Favourite' }}
/>
</Drawer.Navigator>
)
}
const AppContainer = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Root" component={Root} />
<Stack.Screen name="Drawer" component={Drawer} />
//import your Details screen to use inside component
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default AppContainer
You will wrap your App.js component with this AppContainer. What we just did was to nest the navigations, your Home will be reached first and your drawer will be all in the same. Also, there is one missing stack in your code that is the one for the Details.
Right after here you are going to use all the actions from react-navigation. All the screens will receive a navigation props. Once you want to navigate from your Root, you will call the navigation just like props.navigation.navigate("Home").
The same will apply to navigate to your Detail screen from the FlatList.
//Your Home Screen
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
AsyncStorage,
TouchableOpacity,
FlatList,
Button,
SafeAreaView
} from 'react-native';
class HomeScreen extends Component{
constructor(props) {
super(props);
this.state = {
activitiesList: [],
}
};
renderItem = (item) => {
return (
<TouchableOpacity
onPress={() => {
props.navigation.navigate("Details", { data: item })
console.log('test')
}}
>
<View style={styles.item}>
<Text style={styles.title}>{item.name}</Text>
</View>
</TouchableOpacity>
);
}
render(){
const listActivities = this.state.activitiesList
return (
<View>
<View style={styles.container}>
<Text style={styles.heading}>UPCOMING ACTIVITIES</Text>
</View>
<View>
<SafeAreaView>
<FlatList
data = {listActivities}
renderItem={({ item }) => this.renderItem(item)}
keyExtractor={item => item.id}
/>
</SafeAreaView>
</View>
</View>
)
}
}
Just added the navigation action to the code above. You have to access the params object in your Details Screen
//Details Screen
class DetailsScreen extends React.Component {
render() {
const { routes } = this.props;
return (
<View>
//Your component
<Text>{routes.data.name}</Text>
</View>
);
}
}
Just a side note. If you have a problem when navigating to a nested stack you can navigate this way using params
navigation.navigate(<YourParent>, {
screen: <YourDesiredScreen>,
params: { <YourObject> },
});
I hope it helps, at least as a guide. I didn't tested it, that's why I asked you to upload your code to expo.
I use react-navigation and couldn't be happier. All the navigation needed can be done with a simple:
this.props.navigation.navigate('ScreenName', {param1: 'Hello', param2: 'World'})
and then, when on the screen desired, get the param:
const { param1, param2 } = this.props.route.params;
More details here.
And, of course, if using function components it is even easier.
This example is form my 1st react-native-expo project but I hope that it will help you.
Bottom code is for two stack screens where I have PlayersTab screen and Player so main focus is to access Player with parms from PlayersTab using react-navigation
Navigation is in props object all you need is to destructor it in component const PlayersList = ({ navigation }) => {//your code}
import { View, Text } from 'react-native';
import React from 'react';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import PlayersList from '../personal_data_screen/PlayersList';
import PersonalDataScreenPlayer from'../personal_data_screen/PersonalDataScreenPlayer';
const Stack = createNativeStackNavigator();
const PersonalPlayerDataNavigator = ({ loginDataObject }) => {
return (
<Stack.Navigator>
<Stack.Screen options={{ headerShown: false }} name='PlayersTab'>
{(props) => (
<PlayersList {...props} loginDataObject={loginDataObject} />
)}
</Stack.Screen>
<Stack.Screen options={{ headerShown: false }} name='Player'>
{(props) => <PersonalDataScreenPlayer {...props} />}
</Stack.Screen>
</Stack.Navigator>
);
};
export default PersonalPlayerDataNavigator;
FlatList witch is in PlayersTab looks like this
<FlatList
data={Object.keys(friendList.data)}
renderItem={renderItem}
keyExtractor={keyExtractor}
navigation={navigation}
/>
const renderItem = ({ item }) => (
<Item
navigation={navigation}
account_id={friendList.data[item].account_id}
nickname={friendList.data[item].nickname}
/>
const Item = ({ nickname, account_id, navigation }) => (
<TouchableOpacity
onPress={() => navigation.navigate('Player', { account_id: account_id })}>
<View style={styles.itemView}>
<Text style={styles.playerNicknameText}>{nickname}</Text>
</View>
</TouchableOpacity>
And code for Player screen
import React from 'react';
import { View, Text } from 'react-native';
const PersonalDataScreenPlayer = ({ route }) => {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>{route.params.account_id}</Text>
</View>
);
};
export default PersonalDataScreenPlayer;

Categories