如何在 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