React Navigation 在登录时显示不同的选项卡

React Navigation Show Different Tabs On Login

我正在使用带有底部选项卡导航器的反应导航。当应用程序启动时,它会从持久数据存储中获取数据,并根据用户是否登录显示正确的选项卡。但是,一旦用户登录,应用程序必须关闭并重新打开才能显示正确的选项卡。

这是我的 App.js 代码:

import React, { useState } from "react";

import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

import { SimpleLineIcons, Entypo, Feather } from "@expo/vector-icons";
import { useFonts, Recursive_300 } from "@expo-google-fonts/inter";
import { StatusBar } from "expo-status-bar";
import AppLoading from "expo-app-loading";
import * as SecureStore from "expo-secure-store";

import Blogs from "./src/screens/Blogs";
import Blog_Info from "./src/screens/DetailsScreen";
import Login from "./src/screens/Login";
import Sign_Up from "./src/screens/Sign_up";
import Logout from "./src/screens/Logout";
import PostBlog from "./src/screens/NewBlog";

import { getGlobalState, setGlobalState } from "./src/GlobalState";

const Tab = createBottomTabNavigator();

export default function App() {
  let [fontsLoaded] = useFonts({
    Recursive_300,
  });

  const [isChecking, setIsChecking] = useState(true);

  const [isSignedIn, setIsSignedIn] = useState(null);

  async function getUserData() {
    const username = await SecureStore.getItemAsync("blogger101_Username");
    const password = await SecureStore.getItemAsync("blogger101_Password");
    const email = await SecureStore.getItemAsync("blogger101_Email");
    setGlobalState("username", username);
    setGlobalState("password", password);
    setGlobalState("email", email);
    if (username === null) {
      setIsSignedIn(false);
    } else {
      setIsSignedIn(true);
    }
  }

  if (isChecking) {
    return (
      <AppLoading
        startAsync={getUserData}
        onFinish={() => setIsChecking(false)}
        onError={console.warn}
      />
    );
  } else {
    if (getGlobalState("username") === null && isSignedIn !== false) {
      setIsSignedIn(false);
    } else if (getGlobalState("username") !== null && isSignedIn !== true) {
      setIsSignedIn(true);
    }
  }

  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarButton: [
            "Details",
          ].includes(route.name)
            ? () => {
                return null;
              }
            : undefined,
          tabBarIcon: ({ focused, color, size }) => {
            if (route.name === "Blogs") {
              return <Entypo name="text-document" size={size} color={color} />;
            } else if (route.name === "Post_Blog") {
              return <Entypo name="new-message" size={size} color={color} />;
            } else if (route.name === "Login") {
              return <SimpleLineIcons name="login" size={size} color={color} />;
            } else if (route.name === "Sign_Up") {
              return <Feather name="user-plus" size={size} color={color} />;
            } else if (route.name === "Logout") {
              return (
                <SimpleLineIcons name="logout" size={size} color={color} />
              );
            }
          },
          tabBarActiveTintColor: "tomato",
          tabBarInactiveTintColor: "gray",
        })}
      >
        <Tab.Screen
          name="Blogs"
          component={Blogs}
          initialParams={{ message: "" }}
          options={{
            headerLeft: (props) => (
              <Entypo name="text-document" size={26} color="black" />
            ),
            headerLeftContainerStyle: { paddingLeft: 10 },
            headerRightContainerStyle: { paddingRight: 10 },
          }}
        />
  
        {isSignedIn ? (
          <Tab.Screen
            name="Post_Blog"
            component={PostBlog}
            options={{
              headerLeft: (props) => (
                <Entypo name="new-message" size={26} color="black" />
              ),
              headerLeftContainerStyle: { paddingLeft: 10 },
              headerRightContainerStyle: { paddingRight: 10 },
            }}
          />
        ) : <Tab.Screen
            name="Sign_Up"
            component={Sign_Up}
            options={{
              headerLeft: (props) => (
                <Feather name="user-plus" size={20} color="black" />
              ),
              headerLeftContainerStyle: { paddingLeft: 10 },
              headerRightContainerStyle: { paddingRight: 10 },
            }}
          />
        }

        {isSignedIn ? (
          <Tab.Screen name="Logout" component={Logout} />
        ) : <Tab.Screen
            name="Login"
            component={Login}
            options={{
              headerLeft: (props) => (
                <SimpleLineIcons name="login" size={26} color="black" />
              ),
              headerLeftContainerStyle: { paddingLeft: 10 },
              headerRightContainerStyle: { paddingRight: 10 },
            }}
          />
        }
        
        <Tab.Screen name="Details" component={Blog_Info} />
      </Tab.Navigator>
      <StatusBar style="dark" />
    </NavigationContainer>
  );
}

React 导航的 bottomTab 导航器有一个“initialRoute”选项。您可以根据用户是否登录将其设置为不同的路线。

我最终在 Stack Navigator 中 nesting 两个 Tab Navigators。一个选项卡导航器供登录用户使用,另一个供未登录用户使用。

更新后App.js

import "react-native-gesture-handler";

import React, { useState } from "react";
import { Text } from "react-native";

import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createStackNavigator } from '@react-navigation/stack';

import { SimpleLineIcons, Entypo, Feather } from "@expo/vector-icons";
import { useFonts, Recursive_300 } from "@expo-google-fonts/inter";
import { StatusBar } from "expo-status-bar";
import AppLoading from "expo-app-loading";
import * as SecureStore from "expo-secure-store";

import Blogs from "./src/screens/Blogs";
import Blog_Info from "./src/screens/DetailsScreen";
import Login from "./src/screens/Login";
import Sign_Up from "./src/screens/Sign_up";
import Logout from "./src/screens/Logout";
import PostBlog from "./src/screens/NewBlog";

import { getGlobalState, setGlobalState } from "./src/GlobalState";

const LoggedOutTab = createBottomTabNavigator();
const LoggedInTab = createBottomTabNavigator();
const Stack = createStackNavigator();

function LoggedInTabNavigator() {
  return (
    <LoggedInTab.Navigator
      screenOptions={({ route }) => ({
        tabBarButton: route.name === "Details" ? () => null : undefined,
        tabBarIcon: ({ focused, color, size }) => {
          if (route.name === "Blogs") {
            return <Entypo name="text-document" size={size} color={color} />;
          } else if (route.name === "Post_Blog") {
            return <Entypo name="new-message" size={size} color={color} />;
          } else if (route.name === "Login") {
            return <SimpleLineIcons name="login" size={size} color={color} />;
          } else if (route.name === "Sign_Up") {
            return <Feather name="user-plus" size={size} color={color} />;
          } else if (route.name === "Logout") {
            return (
              <SimpleLineIcons name="logout" size={size} color={color} />
            );
          }
        },
        tabBarActiveTintColor: "tomato",
        tabBarInactiveTintColor: "gray",
      })}
    >
      <LoggedInTab.Screen
        name="Blogs"
        component={Blogs}
        initialParams={{ message: "" }}
        options={{
          headerLeft: (props) => (
            <Entypo name="text-document" size={26} color="black" />
          ),
          headerRight: (props) => (
            <Text style={{ fontSize: 16 }}>
              <Feather name="user" size={24} color="black" />
              User: {getGlobalState("username")}
            </Text>
          ),
          headerLeftContainerStyle: { paddingLeft: 10 },
          headerRightContainerStyle: { paddingRight: 10 },
        }}
      />

      <LoggedInTab.Screen
        name="Post_Blog"
        component={PostBlog}
        options={{
          headerLeft: (props) => (
            <Entypo name="new-message" size={26} color="black" />
          ),
          headerRight: (props) => (
            <Text style={{ fontSize: 16 }}>
              <Feather name="user" size={24} color="black" />
              User: {getGlobalState("username")}
            </Text>
          ),
          headerLeftContainerStyle: { paddingLeft: 10 },
          headerRightContainerStyle: { paddingRight: 10 },
        }}
      />

      <LoggedInTab.Screen name="Logout" component={Logout} />
      
      <LoggedInTab.Screen name="Details" component={Blog_Info} />
    </LoggedInTab.Navigator>
  );
}

function LoggedOutTabNavigator() {
  return (
    <LoggedOutTab.Navigator
      screenOptions={({ route }) => ({
        tabBarButton: route.name === "Details" ? () => null : undefined,
        tabBarIcon: ({ focused, color, size }) => {
          if (route.name === "Blogs") {
            return <Entypo name="text-document" size={size} color={color} />;
          } else if (route.name === "Post_Blog") {
            return <Entypo name="new-message" size={size} color={color} />;
          } else if (route.name === "Login") {
            return <SimpleLineIcons name="login" size={size} color={color} />;
          } else if (route.name === "Sign_Up") {
            return <Feather name="user-plus" size={size} color={color} />;
          } else if (route.name === "Logout") {
            return (
              <SimpleLineIcons name="logout" size={size} color={color} />
            );
          }
        },
        tabBarActiveTintColor: "tomato",
        tabBarInactiveTintColor: "gray",
      })}
    >
      <LoggedOutTab.Screen
        name="Blogs"
        component={Blogs}
        initialParams={{ message: "" }}
        options={{
          headerLeft: (props) => (
            <Entypo name="text-document" size={26} color="black" />
          ),
          headerRight: (props) => (
            <Text style={{ fontSize: 16 }}>
              <Feather name="user" size={24} color="black" />
              User: {getGlobalState("username")}
            </Text>
          ),
          headerLeftContainerStyle: { paddingLeft: 10 },
          headerRightContainerStyle: { paddingRight: 10 },
        }}
      />

      <LoggedOutTab.Screen
        name="Sign_Up"
        component={Sign_Up}
        options={{
          headerLeft: (props) => (
            <Feather name="user-plus" size={20} color="black" />
          ),
          headerRight: (props) => (
            <Text style={{ fontSize: 16 }}>
              <Feather name="user" size={24} color="black" />
              User: {getGlobalState("username")}
            </Text>
          ),
          headerLeftContainerStyle: { paddingLeft: 10 },
          headerRightContainerStyle: { paddingRight: 10 },
        }}
      />

      <LoggedOutTab.Screen
        name="Login"
        component={Login}
        options={{
          headerLeft: (props) => (
            <SimpleLineIcons name="login" size={26} color="black" />
          ),
          headerLeftContainerStyle: { paddingLeft: 10 },
          headerRightContainerStyle: { paddingRight: 10 },
        }}
      />
      
      <LoggedOutTab.Screen name="Details" component={Blog_Info} />
    </LoggedOutTab.Navigator>
  );
}


export default function App() {
  let [fontsLoaded] = useFonts({
    Recursive_300,
  });

  const [isChecking, setIsChecking] = useState(true);

  const [isSignedIn, setIsSignedIn] = useState(null);

  async function getUserData() {
    const username = await SecureStore.getItemAsync("blogger101_Username");
    const password = await SecureStore.getItemAsync("blogger101_Password");
    const email = await SecureStore.getItemAsync("blogger101_Email");
    setGlobalState("username", username);
    setGlobalState("password", password);
    setGlobalState("email", email);
    if (username === null) {
      setIsSignedIn(false);
    } else {
      setIsSignedIn(true);
    }
  }

  if (isChecking) {
    return (
      <AppLoading
        startAsync={getUserData}
        onFinish={() => setIsChecking(false)}
        onError={console.warn}
      />
    );
  } else {
    if (getGlobalState("username") === null && isSignedIn !== false) {
      setIsSignedIn(false);
    } else if (getGlobalState("username") !== null && isSignedIn !== true) {
      setIsSignedIn(true);
    }
  }

  return (
    <NavigationContainer>
      <Stack.Navigator
        screenOptions={{
          headerShown: false
        }}
        initialRouteName={isSignedIn ? "LoggedIn" : "LoggedOut"}
      >
        <Stack.Screen
          name="LoggedIn"
          component={LoggedInTabNavigator}
        />
        <Stack.Screen
          name="LoggedOut"
          component={LoggedOutTabNavigator}
        />
      </Stack.Navigator>
      <StatusBar style="dark" />
    </NavigationContainer>
  );
}