如何在 React native 的父导航中导航到屏幕
How to navigate to a screen in parent navigation in React native
我是 React Navigation 的新手,我正在尝试在 react-native 中实现以下功能:
我有一个堆栈导航器作为父项,底部导航栏作为它的子项。在主屏幕上,当用户单击注销选项时,他们应该 return 到作为父导航一部分的登录屏幕。
关于这个已经有很多问题,但我无法在我的代码中实现以前的解决方案。
请大神帮帮我,代码如下(这是一个Expo管理的项目):
导航组件
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import WelcomeScreen from "../screens/WelcomeScreen";
import Features from "../screens/Features";
import SignIn from "../screens/SignIn";
import Register from "../screens/Register";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Icon } from "react-native-elements";
import Home from "../screens/Home";
import Reminders from "../screens/Reminders";
import Settings from "../screens/Settings";
const BottomNavbar = () => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused }) => {
let iconName;
let rn = route.name;
if (rn === "Home") {
iconName = focused ? "home" : "home-outline";
} else if (rn === "Reminders") {
iconName = focused ? "list" : "list-outline";
} else if (rn === "Settings") {
iconName = focused ? "settings" : "settings-outline";
}
return (
<Icon
name={iconName}
size={25}
color="black"
type="ionicon"
/>
);
},
})}
showLabel
>
<Tab.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Reminders"
component={Reminders}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{ headerShown: false }}
/>
</Tab.Navigator>
);
};
const Navigator = () => {
const Stack = createNativeStackNavigator();
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="Welcome"
>
<Stack.Screen name="Welcome" component={WelcomeScreen} />
<Stack.Screen name="Features" component={Features} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="BottomNavbar" component={BottomNavbar} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default Navigator;
主屏幕组件
import {
View,
Text,
SafeAreaView,
StyleSheet,
TouchableOpacity,
ScrollView,
} from "react-native";
import React from "react";
import { Header, Image, Icon } from "react-native-elements";
import { useFonts } from "expo-font";
import ServiceCard from "../components/ServiceCard";
import PetCard from "../components/PetCard";
const SignOut = ({ navigation }) => {
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
const Home = () => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
if (!loaded) {
return null;
}
const url1 =
"https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=388&q=80";
const url2 =
"https://images.unsplash.com/photo-1560807707-8cc77767d783?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=435&q=80";
const url3 =
"https://images.unsplash.com/photo-1543466835-00a7907e9de1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80";
return (
<View style={styles.screen}>
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut />}
centerComponent={{
text: "PetPapa",
color: "black",
style: {
fontFamily: "Montserrat",
fontSize: 20,
},
}}
barStyle="dark-content"
backgroundColor="white"
containerStyle={styles.header}
/>
<View style={styles.community}>
<View style={styles.commOffer}>
<View>
<Text style={styles.commOfferTitle}>Join our</Text>
<Text style={styles.commOfferTitle}>
community today!
</Text>
</View>
<TouchableOpacity style={styles.btn}>
<Text style={styles.commOfferJoin}>Join Now</Text>
</TouchableOpacity>
</View>
<Image
source={{
uri: "https://imgur.com/nB4Xm1Z.png",
}}
style={styles.commDog}
/>
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>Services</Text>
<TouchableOpacity>
<Text style={styles.option}>See more</Text>
</TouchableOpacity>
</View>
<ServiceCard />
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>My Pets</Text>
<TouchableOpacity>
<Text style={styles.option}>See all</Text>
</TouchableOpacity>
</View>
<ScrollView
style={styles.petView}
horizontal={true}
showsHorizontalScrollIndicator={true}
persistentScrollbar={true}
>
<PetCard name="Miles" Img={url1} />
<PetCard name="Jack" Img={url2} />
<PetCard name="Ellie" Img={url3} />
</ScrollView>
</View>
</View>
);
};
const styles = StyleSheet.create({
screen: {
height: "100%",
backgroundColor: "white",
alignItems: "center",
},
header: {
backgroundColor: "white",
height: 80,
},
community: {
backgroundColor: "#1976D2",
height: "15%",
width: "80%",
borderRadius: 20,
marginTop: 50,
flexDirection: "row",
justifyContent: "space-around",
},
commDog: {
marginTop: 10,
marginRight: 15,
height: 105,
width: 75,
},
commOffer: {
marginTop: 10,
flexDirection: "column",
justifyContent: "space-around",
},
commOfferTitle: {
color: "white",
fontFamily: "Montserrat",
fontSize: 16,
},
btn: {
backgroundColor: "#FFC107",
width: "50%",
borderRadius: 10,
},
commOfferJoin: {
color: "white",
margin: 5,
},
listView: {
marginTop: 50,
width: "80%",
},
topText: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
title: {
fontFamily: "Montserrat",
fontWeight: "600",
},
option: {
fontFamily: "Montserrat",
opacity: 0.4,
},
block: {
backgroundColor: "#FF5677",
width: 75,
height: 100,
justifyContent: "center",
alignItems: "center",
marginTop: 25,
borderRadius: 20,
},
petView: {
width: "100%",
backgroundColor: "white",
height: 250,
},
});
export default Home;
我的目录结构:
首先你需要在 Home screen 组件中添加 navigation prop
const Home = ({navigation}) => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
...
然后你需要将导航属性传递给注销组件
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut navigation={navigation} />}
...
您还可以使用 React 导航中的 useNavigation 钩子
import { useNavigation } from '@react-navigation/native';
const SignOut = ({}) => {
const navigation = useNavigation()
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
如果您想创建登录流程,那么您应该使用身份验证流程,我认为这是最佳实践和推荐方式
当前流程中的问题,如果您注销并导航到 sign-in 页面一次,然后如果您从您的应用导航回来,那么作为堆栈导航的行为,它只会弹出当前屏幕并再次导航到主屏幕这不应该是正确的。
可以参考react-navigation官方文档
https://reactnavigation.org/docs/auth-flow/
如果你想要一个视频教程如何使用 redux(状态管理库)实现身份验证流程那么我有视频教程你可以学习这个视频 -> https://youtu.be/cm1oJ7JmW6c
我是 React Navigation 的新手,我正在尝试在 react-native 中实现以下功能: 我有一个堆栈导航器作为父项,底部导航栏作为它的子项。在主屏幕上,当用户单击注销选项时,他们应该 return 到作为父导航一部分的登录屏幕。
关于这个已经有很多问题,但我无法在我的代码中实现以前的解决方案。
请大神帮帮我,代码如下(这是一个Expo管理的项目):
导航组件
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import WelcomeScreen from "../screens/WelcomeScreen";
import Features from "../screens/Features";
import SignIn from "../screens/SignIn";
import Register from "../screens/Register";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Icon } from "react-native-elements";
import Home from "../screens/Home";
import Reminders from "../screens/Reminders";
import Settings from "../screens/Settings";
const BottomNavbar = () => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused }) => {
let iconName;
let rn = route.name;
if (rn === "Home") {
iconName = focused ? "home" : "home-outline";
} else if (rn === "Reminders") {
iconName = focused ? "list" : "list-outline";
} else if (rn === "Settings") {
iconName = focused ? "settings" : "settings-outline";
}
return (
<Icon
name={iconName}
size={25}
color="black"
type="ionicon"
/>
);
},
})}
showLabel
>
<Tab.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Reminders"
component={Reminders}
options={{ headerShown: false }}
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{ headerShown: false }}
/>
</Tab.Navigator>
);
};
const Navigator = () => {
const Stack = createNativeStackNavigator();
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="Welcome"
>
<Stack.Screen name="Welcome" component={WelcomeScreen} />
<Stack.Screen name="Features" component={Features} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="BottomNavbar" component={BottomNavbar} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default Navigator;
主屏幕组件
import {
View,
Text,
SafeAreaView,
StyleSheet,
TouchableOpacity,
ScrollView,
} from "react-native";
import React from "react";
import { Header, Image, Icon } from "react-native-elements";
import { useFonts } from "expo-font";
import ServiceCard from "../components/ServiceCard";
import PetCard from "../components/PetCard";
const SignOut = ({ navigation }) => {
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
const Home = () => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
if (!loaded) {
return null;
}
const url1 =
"https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=388&q=80";
const url2 =
"https://images.unsplash.com/photo-1560807707-8cc77767d783?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=435&q=80";
const url3 =
"https://images.unsplash.com/photo-1543466835-00a7907e9de1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80";
return (
<View style={styles.screen}>
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut />}
centerComponent={{
text: "PetPapa",
color: "black",
style: {
fontFamily: "Montserrat",
fontSize: 20,
},
}}
barStyle="dark-content"
backgroundColor="white"
containerStyle={styles.header}
/>
<View style={styles.community}>
<View style={styles.commOffer}>
<View>
<Text style={styles.commOfferTitle}>Join our</Text>
<Text style={styles.commOfferTitle}>
community today!
</Text>
</View>
<TouchableOpacity style={styles.btn}>
<Text style={styles.commOfferJoin}>Join Now</Text>
</TouchableOpacity>
</View>
<Image
source={{
uri: "https://imgur.com/nB4Xm1Z.png",
}}
style={styles.commDog}
/>
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>Services</Text>
<TouchableOpacity>
<Text style={styles.option}>See more</Text>
</TouchableOpacity>
</View>
<ServiceCard />
</View>
<View style={styles.listView}>
<View style={styles.topText}>
<Text style={styles.title}>My Pets</Text>
<TouchableOpacity>
<Text style={styles.option}>See all</Text>
</TouchableOpacity>
</View>
<ScrollView
style={styles.petView}
horizontal={true}
showsHorizontalScrollIndicator={true}
persistentScrollbar={true}
>
<PetCard name="Miles" Img={url1} />
<PetCard name="Jack" Img={url2} />
<PetCard name="Ellie" Img={url3} />
</ScrollView>
</View>
</View>
);
};
const styles = StyleSheet.create({
screen: {
height: "100%",
backgroundColor: "white",
alignItems: "center",
},
header: {
backgroundColor: "white",
height: 80,
},
community: {
backgroundColor: "#1976D2",
height: "15%",
width: "80%",
borderRadius: 20,
marginTop: 50,
flexDirection: "row",
justifyContent: "space-around",
},
commDog: {
marginTop: 10,
marginRight: 15,
height: 105,
width: 75,
},
commOffer: {
marginTop: 10,
flexDirection: "column",
justifyContent: "space-around",
},
commOfferTitle: {
color: "white",
fontFamily: "Montserrat",
fontSize: 16,
},
btn: {
backgroundColor: "#FFC107",
width: "50%",
borderRadius: 10,
},
commOfferJoin: {
color: "white",
margin: 5,
},
listView: {
marginTop: 50,
width: "80%",
},
topText: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
title: {
fontFamily: "Montserrat",
fontWeight: "600",
},
option: {
fontFamily: "Montserrat",
opacity: 0.4,
},
block: {
backgroundColor: "#FF5677",
width: 75,
height: 100,
justifyContent: "center",
alignItems: "center",
marginTop: 25,
borderRadius: 20,
},
petView: {
width: "100%",
backgroundColor: "white",
height: 250,
},
});
export default Home;
我的目录结构:
首先你需要在 Home screen 组件中添加 navigation prop
const Home = ({navigation}) => {
const [loaded, error] = useFonts({
Montserrat: require("../assets/fonts/Montserrat-Regular.ttf"),
});
...
然后你需要将导航属性传递给注销组件
<Header
leftComponent={{
icon: "menu",
color: "black",
}}
rightComponent={<SignOut navigation={navigation} />}
...
您还可以使用 React 导航中的 useNavigation 钩子
import { useNavigation } from '@react-navigation/native';
const SignOut = ({}) => {
const navigation = useNavigation()
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("SignIn");
}}
>
<Icon name="logout" color="black" size={20} />
</TouchableOpacity>
);
};
如果您想创建登录流程,那么您应该使用身份验证流程,我认为这是最佳实践和推荐方式
当前流程中的问题,如果您注销并导航到 sign-in 页面一次,然后如果您从您的应用导航回来,那么作为堆栈导航的行为,它只会弹出当前屏幕并再次导航到主屏幕这不应该是正确的。
可以参考react-navigation官方文档 https://reactnavigation.org/docs/auth-flow/
如果你想要一个视频教程如何使用 redux(状态管理库)实现身份验证流程那么我有视频教程你可以学习这个视频 -> https://youtu.be/cm1oJ7JmW6c