I keep getting errors that "navigation" is not defined - react-native

I am trying to create a login button that navigates to another page but I keep getting errors that "navigation" is not defined
here's the screenshot of the error:
screenshot here
screenshot here
that's the app component:
export default function App({navigation}) {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Image
source={logo}
style={styles.stretch}
/>
<Image
source={logo2}
style={styles.stretch2}
/>
<Button title="Login" style={[styles.buttonContainer, styles.loginButton,styles.top]} onPress={() => navigation.navigate("Details")} >
<Text style={styles.loginText}>LOG IN</Text>
</Button>
<TouchableHighlight style={[styles.buttonContainer2, styles.loginButton2,styles.top2]} >
<Text style={styles.loginText2}>Register</Text>
</TouchableHighlight>
<NavigationContainer>
<Stack.Navigator >
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
here I got stuck after using the navigation Ref method:
link here

there is no this.props in function components:
export default function App({navigation}) {
const { navigate } = this.props.navigation;
this should be
export default function App({navigation}) {
const { navigate } = navigation;
notice that you already destructured navigation from props in the function signature

Your App component is a React hook, so calling this.props won't work. You can just use navigation from your App's params.
The navigation prop is only available to components wrapped within your <NavigationContainer> component. You'll probably still get an error trying to call navigate from the App component since <NavigationContainer> is a child of it.
Your best bet is to only use the navigation prop on components within a <NavigationContainer> or accessing it like this based on the documentation:
https://reactnavigation.org/docs/navigating-without-navigation-prop
EDIT:
If you want to access navigation outside of the navigation container, you'll need a ref pointing to it.
import React, { useRef } from "react";
export default function App({}) {
const navigationRef = useRef(null)
// Then wherever you wanna call navigate you do this:
// () => navigationRef.current.navigate("Details")
// make sure you do a null check on navigationRef.current if you want to be extra safe
return (
<NavigationContainer ref={navigationRef}>
....
</NavigationContainer>
)
}

Related

Drawer Navigate inside a stack Navigator

Inside my Home (Stack) page I want to create a drawer to show more information options that will be routes, is there a possibility to create a drawer inside this Home(Stack) to navigate to other routes?
import { api } from '../../services/api'
import Cultural from '../Cultural'
import Religioso from '../Religioso'
export default function Home() {
const Drawer = createDrawerNavigator()
const { user } = useContext(AuthContext)
const [teste, setTeste] = useState([])
async function loadingRequest() {
const res = await api.get('/religioso')
setTeste(res.data)
}
useEffect(() => {
loadingRequest()
console.log(teste)
}, [])
console.log(teste)
return (
<SafeAreaView>
<ContainerCard>
<Image style={{ width: '100%', height: 120, backgroundColor: 'red' }} />
</ContainerCard>
<NavigatorContainer>
<Drawer.Navigator>
<Drawer.Screen name='Cultural' component={Cultural} />
<Drawer.Screen name='Religioso' component={Religioso} />
</Drawer.Navigator>
</NavigatorContainer>
</SafeAreaView>
)
}```
Here is the react navigation documentation on nesting navigators.
https://reactnavigation.org/docs/nesting-navigators/
Article TL;DR
Based on your question, it looks like an example of nested navigation in your react native app.
Your root app contains page Home, and this Home page has pages Cultural, Religioso.
You can nest your navigation pages as follows:
function Home() {
return (
<Drawer.Navigator>
<Drawer.Screen name="Cultural" component={Cultural} />
<Drawer.Screen name="Religioso" component={Religioso} />
</Drawer.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
/>
{/** Other stack screens here */}
</Stack.Navigator>
</NavigationContainer>
);
}

React native navigation prop not being passed to the function

React native navigation from stack navigator is not being passed down to the Restaurantsscreen function
This is the function which has to use naviagtion but it cant
export const RestaurantsScreen = ({navigation}) => (
<SafeArea>
<View style={{"backgroundColor": "rgba(255, 255, 255, 255)"}}>
<FlatList
data={[
{ name: 1 }
]}
renderItem={(item) =>{return (
<Pressable onPress={ ()=>console.log(navigation)}>
<RestaurantInfoCard />
</Pressable>
);
}}
and these are the functions where stack navigator is defined
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={Screentwo} /> // this screen is currently loaded and the screentwo is importing the Restaurants screen function inside it as a component
</Stack.Navigator>
</NavigationContainer>
);
}
Kindly help
thanks
You have two solutions :
Past the navigation props from the Screentwo to you're child component RestaurantsScreen
Used the useNavigation hooks directly in your RestaurantsScreen component (see link for this hook : https://reactnavigation.org/docs/use-navigation/)
Example of the first solution:
//Screentwo inherit navigation props direclty, as this component is called in your Stack.Navigator
export const Screentwo = ({navigation}) => {
return(
//As RestaurantsScreen is not called directly in your Stack.Navigator,
//this component will not inherit navigation props,
//you need to past manually navigation props from Screentwo in this way
<RestaurantsScreen navigation={navigation} />
)
}
Example of the second solution with useNavigation :
//Directly in your RestaurantsScreen component
import { useNavigation } from '#react-navigation/native';
//No need to import navigation props
export const RestaurantsScreen = () => {
//Call useNavigation, and navigation can be used in the exact same way as if you past it as a props
const navigation = useNavigation()
return (
<SafeArea>
the rest of your code here
</SafeArea>
)
}

How to current route name in react-navigation inside navigation.js file?

I want to know the current route name but inside the navigation js file, I use the useRoute hook in any component and work well, but I get this error when I use useRoute inside navigation.js
Error: Couldn't find a route object. Is your component inside a screen in a navigator?
navigation.js code example,
export default function Navigation() {
const route = useRoute(); // show error >> Error: Couldn't find a route object. Is your component inside a screen in a navigator?
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
);
}
when I remove useRoute, the Error is gone, But I need to use useRoute or any other way to get current route name inside navigation.js file.
You can pass the navigationContainerRef from the NavigationContainer to the Navigation comomponent to make the navigation object accessible.
Consider the following code snippet.
import { createNavigationContainerRef } from "#react-navigation/native"
export const navigationRef = createNavigationContainerRef()
const App = () => {
return <NavigationContainer
ref={navigationRef}>
<Navigation navigation={navigationRef} />
</NavigationContainer>
}
export default App
Then, inside Navigation.
export default function Navigation({ navigation }) {
const route = navigation.current?.getCurrentRoute()
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
);
}
The current route name get then be accessed using route?.name.
Edit: As jhon antoy correctly pointed out in the comments, this does not update the current route state if we navigate to a different screen. We need to update this ourselves as follows.
export const navigationRef = createNavigationContainerRef();
const App = () => {
const [routeName, setRouteName] = useState();
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
setRouteName(navigationRef.getCurrentRoute().name)
}}
onStateChange={async () => {
const previousRouteName = routeName;
const currentRouteName = navigationRef.getCurrentRoute().name;
console.log("route", currentRouteName)
setRouteName(currentRouteName);
}}
>
<Navigation routeName={routeName} />
</NavigationContainer>
);
}
export default App;
Inside Navigation.
export function Navigation(props) {
const route = props.routeName
console.log(props)
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
);
}
I have made a snack with some simple navigation buttons.
Works perfectly in v6.
In Your Stack, you have to convert the component to child.. in this way:
<RootStack.Screen
name={NameOfYourRoute}
children={props => (
<NameOfYourStack {...props} currentRoute={currentRoute} />
)}
/>

when I'm using DrawerItemList the app crashes

I'm trying to create a drawer menu using react navigation. I want to use a custom DrawerContent, and when I'm using the the app crashes with this error: "undefined is not an object (evaluating 'state.routes')". If I comment this specific line the app just run.
This is my DrawerContent:
import {
DrawerContentScrollView,
DrawerItem,
DrawerItemList,
} from "#react-navigation/drawer";
import React from "react";
import { Text, View, StyleSheet } from "react-native";
export default function DrawerContent(props) {
return (
<View style={{ flex: 1 }}>
<DrawerContentScrollView
{...props}
contentContainerStyle={{ backgroundColor: "#000" }}
>
<DrawerItemList {...props} />
</DrawerContentScrollView>
</View>
);
}
This is my App.js(where the navigation is):
import React from "react";
import Home from "./src/screens/Home.js";
import { NavigationContainer } from "#react-navigation/native";
import { createDrawerNavigator } from "#react-navigation/drawer";
import DrawerContent from "./src/components/DrawerContent.js";
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator
drawerContent={() => <DrawerContent />}
initialRouteName="Home"
>
<Drawer.Screen
options={{ headerShown: false }}
name="Home"
component={Home}
></Drawer.Screen>
</Drawer.Navigator>
</NavigationContainer>
);
You're expecting DrawerItemList to receive a full set of props from Drawer.Navigator, but you're passing it to Drawer.Navigator wrapped in a function that discards all the props it receives:
<Drawer.Navigator
drawerContent={() => <DrawerContent />} // <-- no props passed to DrawerContent
initialRouteName="Home"
>
You need to pass through all props:
<Drawer.Navigator
drawerContent={(props) => <DrawerContent {...props} />} // <-- pass in the props
initialRouteName="Home"
>
Note that you might think you can just pass in the function component to the prop, like this, which looks functionally identical to the above:
<Drawer.Navigator
drawerContent={DrawerContent} // <-- will fail with "rules of hooks" errors
initialRouteName="Home"
>
...but this will fail with "Invalid hook call" errors if it's a functional component that contains hooks, because drawerContent prop treats the passed-in function as a render function, not a function component.
I tried every possible way to solve this error but at last just after restarting my computer i solved the error. Hope it works for you also

How to use navigation.navigate from a component outside the stack.navigation

I have an application using React native where I am using react-navigation (5.2.9).
I built a Stack.Navigator where I've got my screens but I want the Footer component to be outside so it renders in all screens. The problem is, I can't navigate from the footer, which is what I need to do as the footer has a few buttons that should be changing the screen:
const Stack = createStackNavigator();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer>
<Header />
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false
}}
/>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
headerShown: false
}}
/>
</Stack.Navigator>
<Footer />
</NavigationContainer>
</Provider>
);
};
How do I pass the navigation prop to the footer component?
Try this:
Create a new file named: RootNavigation.js
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
// file where you have the navigation
import {navigationRef} from './path/to/RootNavigation';
<NavigationContainer ref={navigationRef}>
.....
<Footer />
</NavigationContainer>
And Footer.js should be something like this:
// Footer.js
import React from 'react';
import {View, Button} from 'react-native';
import * as RootNavigation from './path/to/RootNavigation';
const Footer= () => {
return (
<View>
<Button
title={'Go to'}
onPress={() =>
RootNavigation.navigate('screenName ', {userName: 'Lucy'})
}
/>
</View>
);
};
export default Footer;
For more info you can read the documentation. https://reactnavigation.org/docs/navigating-without-navigation-prop/
The components outside of the Stack.Screen components do not receive the navigation prop. So in this case, you have to get the NavigationContainer using refs in your footer component, as follows:
Create a ref with const myRef = React.createRef()
pass it to the <NavigationContainer ref={myRef}>, and
use that ref to navigate, myRef.current?.navigate(name, params).
It is all explained here. The docs here separate the creation of the ref in a new file, to allow you to import the ref without dependency loops/ issues. As the docs state, you can now call RootNavigation.navigate('ChatScreen', { userName: 'Lucy' }); in any js module, not just in a react component.
In your case though, you don't need the ref in separate file.
const Stack = createStackNavigator();
const navigationRef = React.createRef();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<Header />
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false
}}
/>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
headerShown: false
}}
/>
</Stack.Navigator>
<Footer navigationRef={navigationRef}/>
</NavigationContainer>
</Provider>
);
};
And in <Footer/> use navigationRef.current?.navigate(name, params)
From the documentation.
https://reactnavigation.org/docs/connecting-navigation-prop/
import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '#react-navigation/native';
function GoToButton({ screenName }) {
const navigation = useNavigation();
return (
<Button
title={`Go to ${screenName}`}
onPress={() => navigation.navigate(screenName)}
/>
);
}
I ended up building a component called screen that will just wrap the content of screen and render the header/footer based on props:
import React from 'react';
import { View } from 'native-base';
import style from './style';
import Footer from '../../footer';
import Header from '../../header';
const Screen = ({
footer,
header,
children,
navigation
}) => (
<View style={style.screen}>
{ header && <Header navigation={navigation} />}
{ children }
{ footer && <Footer navigation={navigation} />}
</View>
);
export default Screen;
And wrapping the screens of my apps like this:
<Screen header footer navigation={navigation}>
... screen content
</Screen>
I feel like it is the best way if I can't sort that problem out.
A simple trick to extract navigation out of screenOptions.
function NavWrapper() {
const navigatorRef = useRef<any>();
<Tab.Navigator
screenOptions={({ navigation: nav }) => navigatorRef.current = nav}
>
<Tab.Screen name="screen1" />
</Tab.Navigator>
}
I solve this problem with a global state using React context API:
// When enter in the HomeScreen:
const { setGlobalNavigation, globalNavigation } = useGlobalContext();
const navigation = useNavigation<RemindersNavigationProp>();
useEffect(() => {
if (setGlobalNavigation && !globalNavigation) setGlobalNavigation(navigation);
}, [setGlobalNavigation, globalNavigation, navigation]);
// When want to use:
const { globalNavigation } = useGlobalContext();
const handleNavigate = () =>
globalNavigation.navigate("contactsStack", { screen: "newContact" });