My idea is to have nav.js contain the navigation container for my mobile application, along with all of the screens here (i.e. the homescreen). This file would theoretically let me include buttons in another file that would let me navigate to the screen in the nav.js file that I specified with the button.
I made a constant for this called NavBar and exported it, but when I tried to import into my photo.js file (an example file where I want to include a button that lets me navigate to my 'Tasks' screen), I got the 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}' explicitly. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them.
I tried simply making my own button in photo.js instead of :
<Button title="Go to tasks" color="lightblue" onPress={() => navigation.navigate('Tasks')}/>, but that only gave me the error that the variable navigation could not be found.
nav.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { StyleSheet, View, Button, ScrollView } from 'react-native';
import TaskApp from './taskapp';
import AddTank from './tank';
const NavBar = () => {
const Stack = createNativeStackNavigator();
{/* Homescreen */}
const HomeScreen = ( {navigation} ) => {
return (
<ScrollView style={homeStyle.container}>
<AddTank />
<Button title="Go to tasks" color="lightblue" onPress={() => navigation.navigate('Tasks')}/>
</ScrollView>
)
}
{/* Default config */}
const Config = ( {navigation } ) => {
return (
<View></View>
)
}
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='Home'>
<Stack.Screen name="Home" component={HomeScreen}/>
<Stack.Screen name="Tasks" component={TaskApp}/>
<Stack.Screen name="Config" component={Config}/>
</Stack.Navigator>
</NavigationContainer>
)
}
const homeStyle = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#E8EAED',
},
tasks: {
flex: 1,
height: 30,
width: 30,
backgroundColor: '#E8EAED',
},
});
export default NavBar;
photo.js
import React, {useState, useEffect} from 'react';
import { StyleSheet, Text, View, Button, Image, ImageBackground } from 'react-native';
import { NavigationContainer, StackActions } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import * as ImagePicker from 'expo-image-picker';
import NavBar from './nav';
const AddPhoto = () => {
const add = {uri: 'https://media.discordapp.net/attachments/639282516997701652/976293252082839582/plus.png?width=461&height=461'}
const Stack = createNativeStackNavigator();
const [hasGalleryPermission, setHasGalleryPermission] = useState(null);
const[ image, setImage ] = useState(null);
useEffect(() => {
(async () => {
const galleryStatus = await ImagePicker.requestCameraMediaLibraryPermissionAsync();
setHasGalleryPermission(galleryStatus.status === 'granted');
})();
}, []);
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4,3],
quality:1,
});
console.log(result);
if (!result.cancelled){
setImage(result.uri);
}
};
if (hasGalleryPermission === false){
return <Text>No access to internal storage.</Text>
}
return (
<View style={styles.container}>
{image && <ImageBackground source={{uri: image}} style={styles.tankPhoto}/>}
<Button title="Add photo" color="lightgreen" onPress={() => pickImage()} />
<NavBar />
</View>
)
}
const styles = StyleSheet.create({
container: {
borderColor: '#C0C0C0',
borderWidth: 1,
backgroundColor: '#FFF',
borderRadius: 50,
width: 330,
flex: 1,
alignItems: 'center',
marginLeft: 'auto',
marginRight: 'auto',
margin: 30,
},
tankPhoto: {
flex: 1,
height: 150,
width: 200,
borderWidth: 1,
margin: 25,
},
})
export default AddPhoto;
What am I doing wrong, or is there a better way to do this? What I need:
let me include buttons in another file that would let me navigate to the screen in the nav.js file that I specified with the button.
React native navigation documentation: https://reactnavigation.org/docs/getting-started
Move these lines outside the NavBar component:
const Stack = createNativeStackNavigator();
{/* Homescreen */}
const HomeScreen = ( {navigation} ) => {
return (
<ScrollView style={homeStyle.container}>
<AddTank />
<Button title="Go to tasks" color="lightblue" onPress={() => navigation.navigate('Tasks')}/>
</ScrollView>
)
}
{/* Default config */}
const Config = ( {navigation } ) => {
return (
<View></View>
)
}
I ended up making this work. I defined
const navigation = useNavigation();
in my code (under AddPhoto constant) and imported:
import { useNavigation } from '#react-navigation/native'; in the same file (photo.js).
Related
What I want
I'm trying to animate the react navigation native stack header by changing the background from a transparent to a gray color when the user scrolls down. I was reading the documentation and it suggests using the navigation.setOptions to interact with the screen info.
I am using react-native-reanimated to capture the scroll value and change it when the user interacts with the screen.
The problem
I'm capturing the scroll value and using it inside the setOptions method but it doesn't work, it just doesn't execute the changes.
import React from 'react';
import {
useAnimatedScrollHandler,
useSharedValue,
} from 'react-native-reanimated';
const MyScreen = ({ navigation }) => {
const scrollY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (e) => {
scrollY.value = e.contentOffset.y;
},
});
React.useLayoutEffect(() => {
navigation.setOptions({
headerStyle: {
backgroundColor: scrollY.value > 0 ? 'black' : 'transparent',
},
headerTransparent: scrollY.value === 0,
});
}, [ navigation, scrollY.value ]);
}
Deps
"react-native": "0.67.2",
"react-native-reanimated": "2.9.1",
"#react-navigation/native": "^6.0.6",
"#react-navigation/native-stack": "^6.2.5",
It's possible to animate the Native Stack header, but since only Animated components accept animated styles in Reanimated 2, you'd probably have to create a new component for the header (an Animated.Something)...
We can achieve this by using the header option, which can be found here.
A simple example built with Expo:
import React from "react";
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View } from "react-native";
import { NavigationContainer, useNavigation } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import Animated, {
useSharedValue,
useAnimatedStyle,
useAnimatedScrollHandler,
interpolateColor,
} from "react-native-reanimated";
const Stack = createNativeStackNavigator();
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
},
item: {
padding: 20,
margin: 15,
backgroundColor: "whitesmoke",
},
header: {
paddingTop: 50,
padding: 15,
borderColor: "whitesmoke",
borderBottomWidth: 1,
},
headerTitle: {
fontSize: 20,
fontWeight: "bold",
},
});
function WelcomeScreen() {
const navigation = useNavigation();
const translationY = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler((event) => {
translationY.value = event.contentOffset.y;
});
const aStyle = useAnimatedStyle(() => ({
backgroundColor: interpolateColor(
translationY.value,
[0, 50],
["white", "skyblue"],
"RGB"
),
}));
React.useLayoutEffect(() => {
navigation.setOptions({
header: () => (
<Animated.View style={[styles.header, aStyle]}>
<Text style={styles.headerTitle}>Testing</Text>
</Animated.View>
),
});
}, [aStyle, navigation]);
return (
<View style={styles.container}>
<Animated.ScrollView onScroll={scrollHandler} scrollEventThrottle={16}>
{Array(15)
.fill(0)
.map((_, index) => (
<View style={styles.item} key={`${index}`}>
<Text>Item {`${index}`}</Text>
</View>
))}
</Animated.ScrollView>
<StatusBar style="auto" />
</View>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen component={WelcomeScreen} name="Welcome" />
</Stack.Navigator>
</NavigationContainer>
);
}
Note that in this example, we're passing an Animated.View to the header option, passing our animated style (aStyle) to it as a style.
Also, just like you did, I'm using useAnimatedScrollHandler to track the scroll position, and interpolating (with interpolateColor) the backgroundColor of the header accordingly (between 'white' and 'skyblue', so it's easier to visualize).
Uploaded this example to this Snack so you can easily test it if you want.
I Hope this helps you solve your problem!
I have BottomTab navigator with 2 screens Home and Activity, nested inside a Drawer Navigator. When I switch from one screen to second one using BottomTab, my header of Drawer Navigator hides with flickering effect and same thing happens again when I show it up on previous screen. I am handling headerShown:true and headerShown:false in listeners prop of Tab.Screen using focus and blur of that screen.
It seems like header is rendering after rendering of components below it. This header showing and hiding has more delay if I have multiple components inside both screens.
Snack repo is attached.
https://snack.expo.dev/#a2nineu/bottomtab-inside-drawer
I suggest you use Interaction Manager
As react-native is single threaded, JS gets blocked while running animation which can cause UI to lag and jitter.
Attaching code with changes
import 'react-native-gesture-handler';
import * as React from 'react';
import {
Text,
View,
StyleSheet,
Image,
InteractionManager,
} from 'react-native';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
const Drawer = createDrawerNavigator();
const Tab = createMaterialBottomTabNavigator();
const Home = (props) => {
React.useEffect(() => {
const interactionPromise = InteractionManager.runAfterInteractions(() =>
setShowContent(true)
);
return () => interactionPromise.cancel();
}, []);
const [showContent, setShowContent] = React.useState(false);
return showContent ? (
<View>
<Text>Home</Text>
<Image
source={{
uri: 'https://images.unsplash.com/photo-1596265371388-43edbaadab94?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80',
}}
style={{ height: 500, width: 500 }}
resizeMode={'cover'}></Image>
</View>
) : null;
};
const Activity = () => {
return <Text>Activity</Text>;
};
const BottomTab = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen
name="Activity"
component={Activity}
listeners={({ navigation, route }) => ({
focus: () => {
navigation.getParent().setOptions({
headerShown: false,
});
},
blur: () => {
navigation.getParent().setOptions({
headerShown: true,
});
},
})}
/>
</Tab.Navigator>
);
};
export default function App() {
return (
<View style={styles.container}>
<NavigationContainer>
<Drawer.Navigator screenOptions={{ headerMode: 'float' }}>
<Drawer.Screen
options={{ headerStyle: { height: 100 } }}
name="BottomTab"
component={BottomTab}
/>
</Drawer.Navigator>
</NavigationContainer>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
I have a React app working in expo snack but need to add navigation. When I do, I am getting _this.setState is not a function. Any help is greatly appreciated as this is fairly new to me. Without the navigation in place and utilizing export default class App extends Component, the app builds and runs as it should but I ma having trouble figuring out where/how to declare setState.
Here's the code:
import React, { useEffect, useState } from 'react';
import { View, ActivityIndicator, Text,SafeAreaView,FlatList,ImageBackground,Image,StyleSheet,TouchableOpacity } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import MapView, { Marker } from "react-native-maps";
import axios from 'axios';
const GOOGLE_MAPS_APIKEY = "xxxxxxxx";
function HomeScreen() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
const state = { reports: [] }
const mapMarkers = () => {
return state.reports.map((report) => <Marker
key={report.anum}
coordinate={{ latitude: report.lat, longitude: report.lon }}
title={report.name}
description={report.image}
>
</Marker >)
}
const getMovies = async () => {
fetch('https://xxxxx.cfm?d3d2fcs1d')
.then(res => res.json())
.then(data => {
this.setState({reports: data.reports}, () => {
console.log(data)
}
)})
.catch(console.error)
}
getMovies()
return (
<View style={styles.container}>
<MapView
style={{ ...StyleSheet.absoluteFillObject }}
initialRegion={{
latitude: 34.8527370,
longitude: -82.3933179,
latitudeDelta: .01,
longitudeDelta: .01
}} apikey={GOOGLE_MAPS_APIKEY} >
{mapMarkers()}
</MapView>
</View>
);
}
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={{headerShown: false}}/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
MainContainer: {
justifyContent: 'center',
flex: 1,
paddingTop: 50,
backgroundColor: '#000',
},
logo: {
width: '90%',
height: 159,
marginTop: 4,
justifyContent: 'center',
},
bannerAd: {
width: '100%',
height: 80,
marginBottom: 12,
justifyContent: 'center',
backgroundColor: '#000',
},
imageThumbnail: {
justifyContent: 'center',
alignItems: 'center',
height: 100,
},
});
export default App;
You have a functional component so should not use setState.
You have already a hook to store your data, you should update that setData and where you are trying to use state now, use the data from that hook instead. (const [data, setData] = useState([]);)
Another thing, the way you have getMovies() called it will happen on each render.
Generally for async data fetching you want to use a useEffect hook.
You would want this to happen on mount, and you should also cleanup in case the component becomes unmounted before the call finished.
useEffect(() => {
let controller = new AbortController();
(async () => {
try {
const response = await fetch('https://xxxxx.cfm?d3d2fcs1d', {
signal: controller.signal
});
const data = response.json();
setData(data.reports);
controller = null;
} catch (e) {
// Handle fetch error
}
})();
return () => controller?.abort();
}, []);
I can see that maybe you confused React Functional Components and React Class Components. If you decide to declare your React component a function then you can use React Hooks like useState. But if you rather choose Class components, then you can use this.state or this.setState because there are no React Hooks
To fix these mismatches, your code should look like this instead:
import React, { useEffect, useState } from 'react';
import {
View,
ActivityIndicator,
Text,
SafeAreaView,
FlatList,
ImageBackground,
Image,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import MapView, { Marker } from 'react-native-maps';
import axios from 'axios';
const GOOGLE_MAPS_APIKEY = 'xxxxxxxx';
function HomeScreen() {
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
// EDIT 1: Add react state hook to manage reports
const [reports, setReports] = useState([]);
// EDIT 3: only fetch movies once the screen loaded and not every time it renders
useEffect(() => {
// EDIT 4: run getMovies() asynchronously
(async () => {
await getMovies();
})();
}, []);
const mapMarkers = () => {
return reports.map((report) => (
<Marker
key={report.anum}
coordinate={{ latitude: report.lat, longitude: report.lon }}
title={report.name}
description={report.image}
></Marker>
));
};
const getMovies = async () => {
fetch('https://xxxxx.cfm?d3d2fcs1d')
.then((res) => res.json())
.then((data) => {
// EDIT 2: Update reports state
setReports(data.reports, () => {
console.log(data);
});
})
.catch(console.error);
};
return (
<View style={styles.container}>
<MapView
style={{ ...StyleSheet.absoluteFillObject }}
initialRegion={{
latitude: 34.852737,
longitude: -82.3933179,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
}}
apikey={GOOGLE_MAPS_APIKEY}
>
{mapMarkers()}
</MapView>
</View>
);
}
function DetailsScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={{ headerShown: false }} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
MainContainer: {
justifyContent: 'center',
flex: 1,
paddingTop: 50,
backgroundColor: '#000',
},
logo: {
width: '90%',
height: 159,
marginTop: 4,
justifyContent: 'center',
},
bannerAd: {
width: '100%',
height: 80,
marginBottom: 12,
justifyContent: 'center',
backgroundColor: '#000',
},
imageThumbnail: {
justifyContent: 'center',
alignItems: 'center',
height: 100,
},
});
export default App;
EDIT 1: and EDIT 2: should do the trick.
Now to avoid a loop when your screen loads, it is always recommend not to update the state in the main render function. So I added EDIT 3: to fix that as well.
Furthermore have a look at React Hooks: https://reactjs.org/docs/hooks-state.html
I am using react-navigation 5 in my mobile app and I am stuck in implementing a log-off feature.
The scenario is I have a stack navigator and a bottom tab navigator in my app. The app starts with the stack navigator (Login feature and Reset Password Feature) and on login goes to the dashboard Page which is from the bottom tab navigator. Now on the Dashboard page, I am implementing a logout feature which when clicked should take me to the login page (part of stack navigator), and no matter what I try it keeps giving me errors like these
The action 'RESET' with payload {"index":0,"routes":[{"name":"AuthNavigator"}]} was not handled by any navigator.
Here are my code snippets right from start
Component Called from App.js
import React, { useState, useEffect, useContext } from "react";
import { ActivityIndicator } from "react-native";
import AsyncStorage from '#react-native-async-storage/async-storage';
import { Center } from "../../components/Center";
import { AuthContext } from "../authentication/AuthProvider";
import { NavigationContainer } from "#react-navigation/native";
import { AuthNavigator } from "./AuthNavigator";
import { MainTabNavigator } from "./MainTabNavigator";
export default function App() {
const { user, login } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
useEffect(() => {
// check if the user is logged in or not
//AsyncStorage.removeItem("user") //- uncomment this and refresh emulator to start from login screen
AsyncStorage.getItem("user")
.then(userString => {
if (userString) {
login();
}
setLoading(false);
})
.catch(err => {
console.log(err);
});
}, []);
if (loading) {
return (
<Center>
<ActivityIndicator size="large" />
</Center>
);
}
return (
<NavigationContainer>
{user ? <MainTabNavigator /> : <AuthNavigator />}
</NavigationContainer>
);
}
AuthNavigator.js
import React from "react";
import { createStackNavigator } from '#react-navigation/stack';
import Login from '../authentication/Login';
import ResetPassword from '../authentication/ResetPassword';
import { MainTabNavigator } from "./MainTabNavigator";
const Stack = createStackNavigator();
export const AuthNavigator = () => {
return (
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="ResetPassword" options={{headerTitle: "Reset Password"}} component={ResetPassword} />
</Stack.Navigator>
);
}
MainTabNavigator.js
import * as React from 'react';
import { Text, View, Image, StyleSheet, Platform } from 'react-native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import DashboardView from '../dashboard/DashboardView';
import Search from '../searchLoan/Search';
import { colors } from '../../styles';
const iconHome = require('../../../assets/images/tabbar/home.png');
const iconGrids = require('../../../assets/images/tabbar/grids.png');
const searchIcon = require('../../../assets/images/pages/search_24px.png');
const Tab = createBottomTabNavigator();
const tabData = [
{
name: 'Dashboard',
component: DashboardView,
icon: iconHome,
},
{
name: 'Search',
component: Search,
icon: searchIcon,
},
];
export const MainTabNavigator = () => {
return (
<Tab.Navigator tabBarOptions={{ style: { height: Platform.OS === 'ios' ? 90 : 50 } }}>
{tabData.map((item, idx) => (
<Tab.Screen
key={`tab_item${idx + 1}`}
name={item.name}
component={item.component}
options={{
tabBarIcon: ({ focused }) => (
<View style={styles.tabBarItemContainer}>
<Image
resizeMode="contain"
source={item.icon}
style={[styles.tabBarIcon, focused && styles.tabBarIconFocused]}
/>
</View>
),
tabBarLabel: ({ focused }) => <Text style={{ fontSize: 12, color: focused ? colors.primary : colors.gray }}>{item.name}</Text>,
title: item.name,
}}
/>
))}
</Tab.Navigator>
);
};
const styles = StyleSheet.create({
tabBarItemContainer: {
alignItems: 'center',
justifyContent: 'center',
borderBottomWidth: 2,
borderBottomColor: colors.white,
paddingHorizontal: 10,
bottom: Platform.OS === 'ios' ? -5 : 0,
},
tabBarIcon: {
width: 23,
height: 23,
},
tabBarIconFocused: {
tintColor: colors.primary,
},
});
DashboardView.js
import React , {useContext }from 'react';
import { StyleSheet, View, TouchableOpacity, Text } from 'react-native';
import {Header} from 'react-native-elements';
import AntIcon from "react-native-vector-icons/AntDesign";
import { colors, fonts } from '../../styles';
import AmountDetails from './AmountDetails';
import DashboardFields from './DashboardFields';
import { AuthContext } from "../authentication/AuthProvider";
import { CommonActions } from "#react-navigation/native";
export default function DashboardView(props) {
const appLogOut = () => {
const { logout } = useContext(AuthContext);
console.log('props', props)
if(logout){
// console.log("Navigation Object", navigation)
props.navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: "AuthNavigator" }],
}));
}
}
return (
<View style={styles.container}>
<Header containerStyle = {{backgroundColor: colors.primary}}
centerComponent={{ text: 'Dashboard', style: { color: colors.white, backgroundColor: colors.primary } }}
rightComponent = <TouchableOpacity onPress={appLogOut()}><AntIcon name="logout" color="white" size={25}/></TouchableOpacity>
/>
<View style={styles.container}>
<View>
<AmountDetails />
</View>
<View style={styles.dashboardFields}>
<DashboardFields />
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.gray,
},
dashboardFields: {
marginTop: 20,
},
});
You should try calling the login screen directly, not the whole stack.
CommonActions.reset({
index: 0,
routes: [{ name: "Login" }],
}));
As the other answer said, you have incorrect route name (AuthNavigator).
However, you're conditionally defining screens based on if the user is logged in. You don't need to do anything extra when logging out. Conditionally defining screens means React Navigation can automatically handle which screen to show when the conditional changes.
So you need to remove the code which does reset.
From the docs:
It's important to note that when using such a setup, you don't need to manually navigate to the Home screen by calling navigation.navigate('Home') or any other method. React Navigation will automatically navigate to the correct screen when isSigned in changes - Home screen when isSignedIn becomes true, and to SignIn screen when isSignedIn becomes false. You'll get an error if you attempt to navigate manually.
More details: https://reactnavigation.org/docs/auth-flow/
I have an icon on the right side of my Header. When pressed I want to be transferred to another page. However, it comes up with an error.
This is my 'icon' screen:
import React, { Component } from 'react';
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
const Login = props => {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
onPress={() => {
props.navigation.navigate({routeName: 'Login'});}}>
<Image
source={{
uri:
'https://clipartart.com/images/login-icon-clipart-5.jpg',
}}
style={{
width: 40,
height: 40,
borderRadius: 40 / 2,
marginLeft: 15,
}}
/>
</TouchableOpacity>
</View>
);
}
export default Login;
This is my 'navigation' screen:
import {createStackNavigator} from 'react-navigation-stack';
import {createAppContainer} from 'react-navigation';
import React from 'react';
import Homepage from './screens/Homepage';
import Checkoutpage from './screens/Checkoutpage';
import Filterpage from './screens/Filterpage';
import Locationpage from './screens/Locationpage';
import Menupage from './screens/MenuPage';
import Welcomepage from './screens/Welcomepage';
import Loginpage from './screens/Loginpage';
import Finalpage from './screens/Finalpage';
import Login from './Components/Login';
const Navigation = createStackNavigator({
Home:Homepage,
Checkout: Checkoutpage,
Filter: Filterpage,
Location: Locationpage,
Menu: Menupage,
Welcome: Welcomepage,
Login: Loginpage,
Final: Finalpage
},
{
defaultNavigationOptions: {
headerRight:() => <Login/>
}
}
);
I'm very new to react-native. So if you found the problem, can you please explain thoroughly so I understand. Thank you!!
So it looks like you are expecting the navigation object to be part of the props passed to your <Login/> component. This object is only defined for screen components in react-navigate.
This means that you need to get access to the navigation functionality some other way. Luckily, this library provides you with the useNavigation() hook. So using that in your component would look something like:
// react-navigation v5+
import { useNavigation } from '#react-navigation/native';
const Login = () => {
const navigation = useNavigation();
return (
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
onPress={() => {
navigation.navigate({ routeName: "Login" });
}}
>
<Image
source={{
uri: "https://clipartart.com/images/login-icon-clipart-5.jpg",
}}
style={{
width: 40,
height: 40,
borderRadius: 40 / 2,
marginLeft: 15,
}}
/>
</TouchableOpacity>
</View>
);
};
It seems to me you are using React Navigation v4.x , in order to use the useNavigation hook you need to upgrade to v5.x.
The navigation prop will be passed to all screens by default and you can use the useNavigation hook like #faelks suggested (if needed in other components).
UPGRADE TO v5 FIRST.
Here you have a little example for v5.x version:
import React from 'react'
import { Button, View, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
const Home = ({ navigation }) => (
<View style={styles.component}>
<Button title="Go to login" onPress={() => navigation.navigate('Login')} />
</View>
)
const Login = ({ navigation }) => (
<View style={styles.component}>
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
)
const Main = createStackNavigator()
const mainConfig = {
// configuration for this stack
initialRouteName: "Home",
}
export default props => (
<NavigationContainer>
<Main.Navigator {...mainConfig}>
<Main.Screen name="Home" component={Home} />
<Main.Screen name="Login" component={Login} />
{/* Other screens for this stack */}
</Main.Navigator>
</NavigationContainer>
)
const styles = StyleSheet.create({
component: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})