在React Native中处理导航路线的安装和卸载问题

460 阅读20分钟

在这篇文章中,我们将介绍React Native中导航路线的安装和卸载。你的应用程序的一个预期行为是,一旦满足认证条件,一组新的导航路线只对登录的用户可用,而其他在认证前显示的屏幕被移除,除非用户退出应用程序,否则不能返回。

为了你的应用程序的安全,受保护的路线为你提供了一种方法,在你的应用程序上只向特定的用户显示某些信息/内容,同时限制未经授权的人访问。

我们将在这个项目中使用Expo,因为它将帮助我们专注于手头的问题,而不是担心大量的设置。本文中的步骤完全可以用于一个裸露的React Native应用程序。

你需要对JavaScript和React Native有一定的熟悉度,才能跟上这个教程。以下是你应该已经熟悉的一些重要内容。

  • React Native中的自定义组件(如何创建组件,在组件中接收、传递和使用道具)。阅读更多
  • React导航。阅读更多
  • React Native中的堆栈导航器。阅读多.
  • React Native核心组件的基本知识 (<View/>,<Text/>, etc.) 。阅读更多内容
  • React NativeAsyncStorage 。阅读更多内容。
  • Context API。阅读更多内容。

项目设置和基础认证

如果你是使用expo的新手,不知道如何安装expo,请访问官方文档。一旦安装完成,继续从我们的命令提示符中用expo初始化一个新的React Native项目。

expo init navigation-project

你将会看到一些选项来选择你想要的基本设置方式。

在我们的例子中,让我们选择第一个选项,将我们的项目设置为一个空白文件。现在,等到JavaScript依赖项的安装完成。

一旦我们的应用程序设置好了,我们就可以把我们的目录改为新的项目目录,并在你最喜欢的代码编辑器中打开它。我们需要安装我们将用于AsyncStorage 的库和我们的导航库。在你的终端中的文件夹目录内,粘贴上面的命令,并选择一个模板(blank 也行)来安装我们的项目依赖。

让我们看看这些依赖的每一个是什么。

  • @react-native-community/async-storage
    就像网络上的localStorage一样,它是一个React Native API,用于在设备上以键值对的方式持久化数据。
  • @react-native-community/masked-view, react-native-screens, react-native-gesture-handle
    这些依赖是核心工具,被大多数导航器用来创建应用程序中的导航结构。(阅读更多关于React Native导航的入门知识)。
  • @react-navigation/native
    这是对React Native导航的依赖。
  • @react-navigation/stack
    这是React Native中堆栈导航的依赖关系。
npm install @react-native-community/async-storage @react-native-community/masked-view @react-navigation/native @react-navigation/stack react-native-screens react-native-gesture-handle

要启动应用程序,请在终端的app目录中使用expo start 。一旦应用程序被启动,你可以使用手机中的expo应用程序扫描条形码并查看应用程序,或者如果你有一个安卓模拟器/IOS模拟器,你可以通过它们从expo开发者工具中打开应用程序,当你启动expo应用程序时,浏览器会打开。对于本文中的图像例子,我们将使用Genymotions来查看我们的结果。下面是我们在Genymotions中的最终结果的样子。

文件夹结构

让我们从一开始就创建我们的文件夹结构,这样我们在进行工作时就会更容易操作。

我们首先需要两个文件夹。

  • context
    这个文件夹将存放我们整个应用程序的上下文,因为我们将使用Context API进行全局状态管理。
  • views
    这个文件夹将存放导航文件夹和不同屏幕的视图。

继续前进,在你的项目目录中创建这两个文件夹。

在context文件夹中,创建一个名为authContext的文件夹,并在authContext文件夹中创建两个文件。

  • AuthContext.js
  • AuthState.js。

当我们开始使用Context API时,我们将需要这些文件。

现在进入我们创建的views文件夹,在其中再创建两个文件夹,即。

  • navigation,
  • 屏幕

现在,我们还没有完成,在screen文件夹中,再创建这两个文件夹。

  • postAuthScreens
  • preAuthScreens

如果你按照正确的文件夹设置,你的文件夹结构目前应该是这样的。

创建我们的第一个屏幕

现在让我们在preAuthScreens文件夹中创建我们的第一个屏幕,并将其称为welcomeScreen.js

preAuthScreens > welcomeScreen.js

这是我们的welcomeScreen.js文件的内容。

import React from 'react';
import { View, Text, Button, StyleSheet, TextInput } from 'react-native';

const WelcomeScreen = () => {

  const onUserAuthentication = () => {
    console.log("User authentication button clicked")
  }

  return (
    <View style={styles.container}>
      <Text style={styles.header}>Welcome to our App!</Text>
      <View>
        <TextInput style={styles.inputs} placeholder="Enter your email here.." />
        <TextInput style={styles.inputs} secureTextEntry={true} placeholder="Enter your password here.." />
<Button  title="AUTHENTICATE" onPress={onUserAuthentication} />
      </View>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  header: {
    fontSize: 25,
    fontWeight: 'bold',
    marginBottom: 30
  },
  inputs: {
    width: 300,
    height: 40,
    marginBottom: 10,
    borderWidth: 1,
  }
})

export default WelcomeScreen

下面是我们在上面的代码块中做的事情。

首先,我们从React Native库中导入了我们需要的东西,即ViewTextButtonTextInput 。接下来,我们创建了我们的功能组件WelcomeScreen

你会注意到,我们从React Native导入了StyleSheet ,并使用它来定义我们的头的样式,还有我们的<TextInput />

最后,我们在代码的底部导出了WelcomeScreen 组件。

现在我们完成了这些,让我们通过使用useState 钩子来存储输入的值,并在输入字段发生变化时随时更新它们的状态,从而使这个组件按照预期的方式运行。我们还将从React中导入useCallback 钩子,因为我们以后将需要它来保持一个函数。

首先,当我们还在WelcomeScreen 组件中时,我们需要从React导入useStateuseCallback

import React, { useState, useCallback } from 'react';

现在在WelcomeScreen 功能组件中,让我们为电子邮件和密码分别创建两个状态。

...
const WelcomeScreen = () => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  return (
    ...
  )
}
...

接下来,我们需要修改我们的<TextInput /> 字段,使其从各自的状态中获取数值,并在输入值更新时更新其状态。

import React, { useState, useCallback } from 'react';
import { View, Text, Button, StyleSheet, TextInput } from 'react-native';

const WelcomeScreen = () => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const onInputChange = (value, setState) => {
    setState(value);
  }
  return (
    <View>
      ...      <View>
        <TextInput
          style={styles.inputs}
          placeholder="Enter your email here.."
          value={email}
          onChangeText={(value) => onInputChange(value, setEmail)}
        />
        <TextInput
          style={styles.inputs}
          secureTextEntry={true}
          placeholder="Enter your password here.."
          value={password}
          onChangeText={(value) => onInputChange(value, setPassword)}
        />
        ...
      </View>
    </View>
  )
}
...

在上面的代码中,我们是这样做的。

  • 我们使每个文本输入的value ,以指向它们各自的状态。
  • 我们给文本输入添加了onChangeText 处理程序。这个处理程序在输入字段中输入或删除一个新值时启动。
  • 我们调用了我们的onInputChange ,该函数接受两个参数。
    • 当前的value 是由onChangeText 处理程序提供的。
    • 应该被更新的状态的设置器(对于第一个输入字段我们传递setEmail ,第二个字段我们传递setPassword
    • 最后,我们编写我们的onInputChange 函数,我们的函数只做一件事:用新的值更新各自的状态。

我们需要做的下一件事是onUserAuthentication() ,每当点击表单提交的按钮时就会调用这个函数。

理想情况下,用户必须已经创建了一个账户,而登录将涉及一些后台逻辑,以检查用户是否存在,然后给用户分配一个令牌。在我们的案例中,由于我们没有使用任何后台,我们将创建一个持有正确的用户登录细节的对象,然后只有当用户输入的值与我们将创建的emailpassword 的登录对象中的固定值相匹配时,才会对用户进行认证。

下面是我们需要做的代码。

...

const correctAuthenticationDetails = {
  email: 'demouser@gmail.com',
  password: 'password'
}
const WelcomeScreen = () => {
  ...

  // This function gets called when the AUTHENTICATE button is clicked
  const onUserAuthentication = () => {
    if (
      email !== correctAuthenticationDetails.email ||
      password !== correctAuthenticationDetails.password
    ) {
      alert('The email or password is incorrect')
      return
    }
      // In here, we will handle what happens if the login details are       // correct
  }

  ...
  return (
    ...
  )
}
...

在上面的代码中,你会注意到的第一件事是,我们在WelcomeScreen() 功能组件之外定义了一个correctAuthenticationDetails (这是一个持有我们期望用户提供的正确登录细节的对象)。

接下来,我们编写了onUserAuthentication() 功能的内容,并使用条件语句来检查在各自状态下持有的emailpassword 是否与我们在对象中提供的不一致。

如果你想看看我们到目前为止所做的事情,请像这样把WelcomeScreen组件导入你的App.js中。

打开App.js文件,把这个把整个代码替换成这样。

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { View } from 'react-native';
import WelcomeScreen from './views/screens/preAuthScreens/welcomeScreen';
export default function App() {
  return (
    <View>
      <StatusBar style="auto" />
      <WelcomeScreen />
    </View>
  );
}

仔细观察上面的代码,你会发现我们所做的是导入WelcomeScreen组件,然后在App() 函数中使用它。

下面是我们的WelcomeScreen 的结果,看起来是这样的。

现在我们已经完成了WelcomeScreen组件的构建,让我们继续前进,开始使用Context API来管理我们的全局状态。

为什么是Context API?

使用Context API,我们不需要在ReactJS中安装任何额外的库,它的设置压力较小,而且是ReactJS中处理全局状态的最流行的方式之一。对于轻量级的状态管理,它是一个不错的选择。

创建我们的上下文

如果你还记得,我们之前创建了一个上下文文件夹,并在其中创建了一个子文件夹,叫做authContext

现在让我们导航到authContext文件夹中的AuthContext.js文件并创建我们的上下文。

context > authContext > AuthContext.js


import React, { createContext } from 'react';
const AuthContext = createContext();
export default AuthContext;

我们刚刚创建的AuthContext ,持有loading 状态值和userToken 状态值。目前,在我们在上面的代码块中声明的createContext ,我们没有在这里初始化任何默认值,所以我们的上下文目前是undefined 。 auth上下文的一个例子值可以是{loading: false, userToken: 'abcd}

AuthState.js文件持有我们的Context API逻辑和它们的状态值。写在这里的函数可以从我们应用程序的任何地方调用,当它们更新状态值时,也会在全局范围内更新。

首先,让我们在这个文件中引入所有我们需要的导入。

context > AuthContext > AuthState.js

import React, { useState } from 'react';
import AuthContext from './AuthContext';
import AsyncStorage from '@react-native-community/async-storage';

我们从ReactJS导入了useState() 钩子来保存我们的状态,我们导入了上面创建的AuthContext文件,因为这是我们用于认证的空上下文被初始化的地方,我们将需要使用它,你将在后面的进展中看到,最后我们导入AsyncStorage 包(类似于web的localStorage)。

AsyncStorage 是一个React Native API,允许你在React Native应用中通过设备离线保存数据。

...

const AuthState = (props) => {
    const [userToken, setUserToken] = useState(null);
    const [isLoading, setIsLoading] = useState(true);

    const onAuthentication = async() => {
        const USER_TOKEN = "drix1123q2"
        await AsyncStorage.setItem('user-token', USER_TOKEN);
        setUserToken(USER_TOKEN);
        console.warn("user has been authenticated!")
    }

    return (
        <AuthContext.Provider
            value={{
                onAuthentication,
            }}
        >
            {props.children}
        </AuthContext.Provider>
    )
}
export default AuthState;

在上面的代码块中,我们是这样做的。

  • 我们声明了两个状态,分别是userTokenisLoadinguserToken 状态将用于存储保存到AsyncStorage 的令牌,而isLoading 状态将用于跟踪加载状态(最初它被设置为true )。我们将在接下来的工作中发现更多关于这两个状态的使用。

  • 接下来,我们编写了我们的onAuthentication() 函数。这个函数是一个async ,当登录按钮从welcomeScreen.jsx 文件中被点击时,这个函数会被调用。只有当用户提供的电子邮件和密码与我们提供的正确的用户细节对象相匹配时,这个函数才会被调用。通常情况下,在认证过程中发生的事情是,当用户在后端使用像JWT这样的包进行认证后,为用户生成一个令牌,这个令牌被发送到前端。由于我们在本教程中不打算讨论这些,我们创建了一个静态令牌,并将其保存在一个名为USER_TOKEN 的变量中。

  • 接下来,我们使用await 关键字,将我们的用户令牌设置为AsyncStorage,名称为user-tokenconsole.warn() 语句只是用来检查一切是否正常,你可以随时把它拿掉。

  • 最后,我们把我们的onAuthenticated 函数作为一个值传给我们的<AuthContext.Provider> ,这样我们就可以从我们的应用程序的任何地方访问和调用这个函数。

screens > preAuth > welcomeScreen.js

首先,从ReactJS中导入useContext ,从AuthContext.js 文件中导入AuthContext

import React, { useState, useContext } from 'react';
import AuthContext from '../../../context/authContext/AuthContext'
...

现在,在welcomeScreen() 功能组件里面,让我们使用我们已经创建的上下文。

...
const WelcomeScreen = () => {
  const { onAuthentication } = useContext(AuthContext)
  const onUserAuthentication = () => {
    if (
      email !== correctAuthenticationDetails.email ||
      password !== correctAuthenticationDetails.password
    ) {
      alert('The email or password is incorrect')
      return
    }
    onAuthentication()
  }
  return (
    ...
  )
}
...

在上面的代码块中,我们从我们的AuthContext 中解构了onAuthentication 函数,然后我们在我们的onUserAuthentication() 函数中调用了它,并删除了之前存在的console.log() 语句。

现在,这将抛出一个错误,因为我们还没有访问AuthContext 。要在你的应用程序中的任何地方使用AuthContext ,我们需要用AuthState (在我们的例子中,它是App.js文件)来包装我们应用程序中的顶级文件。

进入App.js文件,把那里的代码替换成这样。

import React from 'react';
import WelcomeScreen from './views/screens/preAuthScreens/welcomeScreen';
import AuthState from './context/authContext/AuthState'

export default function App() {
  return (
    <AuthState>
      <WelcomeScreen />
    </AuthState>
  );
}

我们已经走到了这一步,这一部分已经完成了。在我们进入下一节,设置我们的路由之前,让我们创建一个新的屏幕。我们将要创建的屏幕将是HomeScreen.js文件,它应该只在认证成功后才显示出来。

转到:屏幕 > postAuth。

创建一个名为HomeScreen.js的新文件。下面是HomeScreen.js文件的代码。

screen > postAuth > HomeScreen.js

import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const HomeScreen = () => {

  const onLogout = () => {
    console.warn("Logout button cliked")
  }

  return (
    <View style={styles.container}>
      <Text>Now you're authenticated! Welcome!</Text>
      <Button title="LOG OUT" onPress={onLogout} />
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
})

export default HomeScreen

现在,注销按钮有一个假的console.log() 语句。稍后,我们将创建注销功能,并从我们的上下文中将其传递给屏幕。

设置我们的路线

我们需要在我们的导航文件夹中创建三(3)个文件。

  • postAuthNavigator.js
  • preAuthNavigator.js
  • **AppNavigator.**js。

一旦你创建了这三个文件,导航到你刚刚创建的preAuthNaviagtor.js文件,然后这样写。

navigation > preAuthNavigator.js

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import WelcomeScreen from "../screens/preAuthScreens/welcomeScreen";

const PreAuthNavigator = () => {
    const { Navigator, Screen } = createStackNavigator();

    return (
        <Navigator initialRouteName="Welcome">
            <Screen
                name="Welcome"
                component={WelcomeScreen}
            />
        </Navigator>
    )
}
export default PreAuthNavigator;

在上面的文件中,我们是这样做的。

  • 我们从@react-navigation/stack 中导入了createStackNavigator ,我们将其用于我们的堆栈导航。createStackNavigator为你的应用程序提供了一种在屏幕之间过渡的方式,每个新的屏幕都被放在堆栈的顶部。默认情况下,堆栈导航器被配置为具有熟悉的iOS和Android的外观和感觉:新屏幕在iOS中从右边滑入,在Android中从底部淡入。如果你想了解更多关于React Native中的堆栈导航器,请点击这里
  • 我们将NavigatorScreencreateStackNavigator()
  • 在我们的返回语句中,我们用<Navigator/> 创建了我们的导航,用<Screen/> 创建了我们的屏幕。这意味着,如果我们有多个屏幕可以在认证前访问,我们将在这里有多个<Screen/> 标签代表它们。
  • 最后,我们导出我们的PreAuthNavigator 组件。

让我们对postAuthNavigator.js 文件做一个类似的事情。

navigation > postAuthNavigator.js

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import HomeScreen from "../screens/postAuthScreens/HomeScreen";
const PostAuthNavigator = () => {
  const { Navigator, Screen} = createStackNavigator();
  return (
    <Navigator initialRouteName="Home">
      <Screen
        name="Home"
        component={HomeScreen}
      />
    </Navigator> 
  )
}
export default PostAuthNavigator;

正如我们在上面的代码中看到的,preAuthNavigator.jspostAuthNavigator.js之间的唯一区别是被渲染的屏幕。前者使用WelcomeScreen ,而postAuthNavigator.js则使用HomeScreen

为了创建我们的AppNavigator.js,我们需要创建一些东西。

由于AppNavigator.js是我们将切换和检查哪条路线可供用户访问的地方,我们需要几个屏幕到位才能正常工作,让我们先概述一下我们需要创建的东西。

  1. TransitionScreen.js
    当应用程序决定它要安装哪个导航时,我们希望显示一个过渡屏幕。通常情况下,过渡屏幕将是一个加载旋钮或为应用程序选择的任何其他自定义动画,但在我们的案例中,我们将使用一个基本的<Text/> 标签来显示 loading….
  2. checkAuthenticationStatus()
    这个函数是我们要调用的,以检查认证状态,这将决定哪个导航栈将被安装。我们将在我们的上下文中创建这个函数并在Appnavigator.js中使用它。

现在,让我们继续创建我们的TransitionScreen.js文件。

屏幕 > TransitionScreen.js

import React from 'react';
import { Text, View } from 'react-native';

const TransitionScreen = () => {
  return (
    <View>
      <Text>Loading...</Text>
    </View>
  )
}

export default TransitionScreen

我们的过渡屏幕只是一个简单的屏幕,显示加载文本。当我们在本文中进行时,我们将看到在哪里使用它。

接下来,让我们进入我们的AuthState.js,写下我们的checkAuthenticationStatus()

context > authContext > AuthState.js

import React, { useState, useEffect } from 'react';
import AuthContext from './AuthContext';
import AsyncStorage from '@react-native-community/async-storage';

const AuthState = (props) => {
    const [userToken, setUserToken] = useState(null);
    const [isLoading, setIsLoading] = useState(true);

    ...
    useEffect(() => {
        checkAuthenticationStatus()
    }, [])

    const checkAuthenticationStatus = async () => {
        try {
            const returnedToken = await AsyncStorage.getItem('user-toke             n');
            setUserToken(returnedToken);
            console.warn('User token set to the state value)
        } catch(err){
            console.warn(Here's the error that occured while retrievin             g token: ${err}) 
        }
        setIsLoading(false)
    }


    const onAuthentication = async() => {
        ...
    }

    return (
        <AuthContext.Provider
            value={{
                onAuthentication,
                userToken,
                isLoading,
            }}
        >
            {props.children}
        </AuthContext.Provider>
    )
}
export default AuthState;

在上面的代码块中,我们写了函数checkAuthenticationStatus() 。在我们的函数中,以下是我们正在做的事情。

  • 我们使用await 关键字,从AsyncStorage 获取我们的令牌。通过AsyncStorage ,如果没有提供令牌,它将返回null 。我们的初始userToken 状态也被设置为null
  • 我们使用setUserToken ,将我们从AsyncStorage 返回的值设置为我们新的userToken 。如果返回值是null ,这意味着我们的userToken 仍然是null
  • try{}…catch(){} 块之后,我们将isLoading 设置为false,因为检查认证状态的函数已经完成。我们需要isLoading 的值来知道我们是否还应该显示TransitionScreen 。值得考虑的是,如果检索令牌时出现错误,那么我们可以在遇到错误时向用户显示一个 "重试 "或 "再试 "按钮。
  • 每当AuthState ,我们想检查认证状态,所以我们使用useEffect() ReactJS钩子来做这个。我们在useEffect() 钩子里面调用我们的checkAuthenticationStatus() 函数,完成后将isLoading 的值设置为false
  • 最后,我们将我们的状态添加到我们的<AuthContext.Provider/> 值中,这样我们就可以从Context API覆盖的应用程序的任何地方访问它们。

现在我们有了我们的函数,现在是时候回到我们的AppNavigator.js,并编写代码,根据认证状态安装一个特定的堆栈导航器。

navigation > AppNavigator.js

首先,我们将导入我们的AppNavigator.js所需要的一切。

import React, { useEffect, useContext } from "react";
import PreAuthNavigator from "./preAuthNavigator";
import PostAuthNavigator from "./postAuthNavigator";
import { NavigationContainer } from "@react-navigation/native"
import { createStackNavigator } from "@react-navigation/stack";
import AuthContext from "../../context/authContext/AuthContext";
import TransitionScreen from "../screens/TransitionScreen";

现在我们有了所有的导入,让我们创建AppNavigator() 函数。

...
const AppNavigator = () => {

}

export default AppNavigator

接下来,我们现在将继续编写我们的AppNavigator() 函数的内容。

import React, { useState, useEffect, useContext } from "react";
import PreAuthNavigator from "./preAuthNavigator";
import PostAuthNavigator from "./postAuthNavigator";
import { NavigationContainer } from "@react-navigation/native"
import { createStackNavigator } from "@react-navigation/stack";
import AuthContext from "../../context/authContext/AuthContext";
import TransitionScreen from "../screens/transition";

const AppNavigator = () => {
    const { Navigator, Screen } = createStackNavigator();
    const authContext = useContext(AuthContext);
    const { userToken, isLoading } = authContext;
    if(isLoading) {
      return <TransitionScreen />
    }
    return (
    <NavigationContainer>
      <Navigator>
        { 
          userToken == null ? (
            <Screen
              name="PreAuth"
              component={PreAuthNavigator}
              options={{ header: () => null }}
            />
          ) : (
            <Screen 
              name="PostAuth"
              component={PostAuthNavigator}
              options={{ header: () => null }}
            />
          )
        }
      </Navigator>
    </NavigationContainer>
  )
}

export default AppNavigator

在上面的代码块中,以下是我们所做工作的概要。

  • 我们创建了一个堆栈导航器,并从中解构了NavigatorScreen
  • 我们将userTokenisLoading 从我们的 "堆栈导航器 "中导入。AuthContext
  • AuthState 挂载时,checkAuthenticationStatus()useEffecct 钩子那里被调用。我们使用if 语句来检查isLoading 是否是true ,如果是true ,我们返回的屏幕就是我们之前创建的<TransitionScreen /> ,因为checkAuthenticationStatus() 函数还没有完成。
  • 一旦我们的checkAuthenticationStatus() 完成,isLoading 被设置为false ,我们返回我们的主导航组件。
  • NavigationContainer 是从@react-navigation/native 中导入的。它只在主顶层导航器中使用一次。注意,我们没有在preAuthNavigator.jspostAuthNavigator.js中使用它**。**
  • 在我们的AppNavigator() ,我们仍然创建了一个堆栈导航器。如果从我们的Context API得到的userTokennull ,我们就加载PreAuthNavigator ,如果它的值是其他的(意味着checkAuthenticationStatus() 中的AsyncStorage.getItem() 返回一个实际值),那么我们就加载PostAuthNavigator 。我们的条件性渲染是使用三元操作符完成的。

现在我们已经设置了我们的AppNavigator.js。接下来,我们需要将我们的AppNavigator 传入我们的App.js文件。

让我们把我们的AppNavigator 传递到App.js文件中。

App.js

 ...
import AppNavigator from './views/navigation/AppNavigator';

...
return (
    <AuthState>
      <AppNavigator />
    </AuthState>
  );

现在让我们看看我们的应用程序目前是什么样子的。

下面是当你在试图登录时提供了一个错误的凭证时发生的情况。

添加注销功能

在这一点上,我们的认证和路由选择过程已经完成。我们的应用程序剩下的唯一事情就是添加注销功能。

注销按钮在HomeScreen.js文件中。我们向按钮的onPress 属性传递了一个onLogout() 函数。现在,我们的函数中只有一个简单的console.log() 语句,但过一会儿就会改变。

现在,让我们进入我们的AuthState.js文件,写出注销的函数。这个函数只是清除了保存用户令牌的AsyncStorage

context > authContext > AuthState.js

...
const AuthState = (props) => {
    ...

    const userSignout = async() => {
        await AsyncStorage.removeItem('user-token');
        setUserToken(null);
    }


    return (
      ...
    )
}

export default AuthState;

userSignout() 是一个异步函数,将user-token 从我们的AsyncStorage 中删除。

现在我们需要在任何时候点击注销按钮时在我们的HomeScreen.js中调用userSignout() 函数。

让我们到HomeScreen.js中,使用userSignout() ,从我们的AuthContext

screen > postAuthScreens > 主页屏幕.js

import React, { useContext } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import AuthContext from '../../../context/authContext/AuthContext'

const HomeScreen = () => {
  const { userSignout } = useContext(AuthContext)

  const onLogout = () => {
    userSignout()
  }
  return (
    <View style={styles.container}>
      <Text>Now you're authenticated! Welcome!</Text>
 <Button title="LOG OUT" onPress={onLogout} />
    </View>
  )
}
...

在上面的代码块中,我们从ReactJS导入了useContext 钩子,然后我们导入了我们的AuthContext。接下来,我们从我们的AuthContext 中解构了userSignout 函数,这个userSignout() 函数被调用到我们的onLogout() 函数中。

现在只要我们的注销按钮被点击,我们的AsyncStorage 中的用户令牌就会被清除。

瞧!我们的整个过程就完成了。

下面是你登录后按下后退按钮时发生的情况。

下面是当你注销后按下后退按钮会发生什么。

下面是我们在导航栈切换中使用这种模式时注意到的一些不同行为。

  1. 你会注意到,我们没有任何地方需要利用navigation.navigate()navigation.push() ,在登录后转到另一条路线。一旦我们的状态被用户令牌更新,呈现的导航栈就会自动改变。
  2. 在登录成功后,在你的设备上按下后退按钮不能带你回到登录页面,相反,它会完全关闭应用程序。这种行为很重要,因为你不希望用户能够返回到登录页面,除非他们退出应用程序。同样的事情也适用于注销--一旦用户注销,他们就不能使用后退按钮返回到HomeScreen ,而是关闭应用程序。

总结

在许多应用程序中,认证是最重要的部分之一,因为它确认了试图访问受保护内容的人有权访问该信息。学习如何正确地做它是建立一个伟大的、直观的、易于使用/导航的应用程序的重要步骤。

在这段代码的基础上,这里有一些你可以考虑添加的东西。

这里还有一些我发现的重要资源,可以让你对认证、安全以及如何正确地进行认证有更多的了解。

资源