前端 路由权限react_如何使用路由和身份验证构建React Hooks前端应用

本文详细介绍了如何使用React Hooks、身份验证和路由构建前端应用程序。通过目录结构、全局状态管理、身份验证和路由设置,阐述了构建具有完整功能的React应用的步骤。使用Auth0进行身份验证,并提供了GitHub上的代码模板链接。
摘要由CSDN通过智能技术生成

前端 路由权限react

In this tutorial, we will go over how to build a complete front end app with routing and authentication.

在本教程中,我们将介绍如何使用路由和身份验证来构建完整的前端应用程序。

I have structured this tutorial and project as basically a boilerplate project with basic routing and auth that can be used as a starter project.

我已将本教程和项目基本上构造为具有基本路由和auth的样板项目,可用作入门项目。

If you just want the boiler plate code without the explanations here it is:https://github.com/iqbal125/react-hooks-routing-auth-starter

如果您只是想要没有说明的样板代码,则为: https : //github.com/iqbal125/react-hooks-routing-auth-starter

I will use Auth0 for authentication, but this setup will work with any other token-based authentication system as well.You can watch a fullstack video version of this tutorial herehttps://www.youtube.com/playlist?list=PLMc67XEAt-yzxRboCFHza4SBOxNr7hDD5

我将使用Auth0进行身份验证,但此设置也可以与任何其他基于令牌的身份验证系统一起使用。您可以在此处https://www.youtube.com/playlist?list=PLMc67XEAt-观看本教程的完整视频版本yzxRboCFHza4SBOxNr7hDD5

Connect with me on Twitter for more updates on future tutorials: https://twitter.com/iqbal125sf

在Twitter上与我联系以获取未来教程的更多更新: https//twitter.com/iqbal125sf

目录 (Table of Contents)

  1. Project Structure

    项目结构
  2. useReducer vs useState for Global Context state

    useReducer与useState的全局上下文状态
  3. Global State with Context

    具有上下文的全局状态
  4. Authentication and authcheck

    身份验证和授权检查
  5. React Hooks Components

    React Hooks组件
  6. Routing

    路由
  7. App.js

    App.js

项目结构 (Project Structure)

I will first go over the structure of our app. Our app can be broken down into 4 parts:

我将首先介绍我们应用程序的结构。 我们的应用程序可以分为4部分:

  • React hooks functional components

    React钩子功能组件
  • Reducers and Actions

    减速器和动作
  • Utility files

    实用程序文件
  • Main files

    主要档案

We will also need 4  libraries to build our app

我们还将需要4个库来构建我们的应用程序

npm install auth0-js react-router react-router-dom history

npm install auth0-js react-router react-router-dom history

目录结构: (Directory Structure:)

React Hooks功能组件 (React Hooks functional components)

Here we have our React Hooks functional components. We have a fairly simple setup and we will not be using any React Class components in this app.

在这里,我们有我们的React Hooks功能组件。 我们的设置非常简单,我们不会在此应用程序中使用任何React Class组件。

callback.js: will be used as the component that Auth0 will redirect to after the user authenticates.

callback.js:将用作用户验证后Auth0重定向到的组件。

header.js: will contain the links to the components and a login or logout button based on the user authentication state.

header.js:将包含指向组件的链接以及基于用户身份验证状态的登录或注销按钮。

home.js: will simply display the text of home.

home.js:只会显示home的文本。

hook1.js: will contain all three ways to update state with React hooks, useState, useReducer and useContext. Having all three ways to update the state in one component will make it easier for you to pick apart the differences between each.

hook1.js:将包含所有三种使用React钩子更新状态的方法 useStateuseReduceruseContext 。 具有在组件中更新状态的所有三种方法,将使您更容易区分每个组件之间的差异。

hooks_form1.js: Will have a form that has all three ways to update state with useState, useReducer and useContext.

hooks_form1.js:将具有一种形式,该形式具有使用useStateuseReduceruseContext更新状态的所有三种方式。

privatecomponent.js: A component that is only accessible by authenticated users.

privatecomponent.js:只能由经过身份验证的用户访问的组件。

profile.js: A user dashboard that displays user profile data.

profile.js:一个显示用户个人资料数据的用户仪表板。

减速器和动作文件 (Reducers and Actions files)

action_types.js: Will hold all the string actions types in variables. This will allow easy modifying of your action types since you will only have to change them here instead having to track down where ever you used the action in your code.

action_types.js:将所有字符串操作类型保存在变量中。 这将使您可以轻松地修改操作类型,因为您只需在此处进行更改即可,而不必跟踪您在代码中使用过操作的位置。

actions.js: Will hold the actual actions that are going to be used in the reducer to update the state.

actions.js:将保存将在reducer中用于更新状态的实际操作。

auth_reducer.js: Will hold the reducer to read and update state properties related authentication.

auth_reducer.js:将持有化器以读取和更新与身份验证相关的状态属性。

form_reducer.js: Will hold the reducer to read and update state properties related to our form.

form_reducer.js:将持有化器以读取和更新与表单相关的状态属性。

plain_reducer.js: Will serve as a boilerplate reducer.

plain_reducer.js:将用作样板还原器。

实用程序文件 (Utility Files)

We will also need 4 utility files to help setup our app.

我们还将需要4个实用程序文件来帮助设置我们的应用程序。

context.js: Will hold the Context object and will be imported to every component that uses the useContext() hook.

context.js:将保存Context对象,并将其导入使用useContext()挂钩的每个组件中。

auth.js: This will be the only class in the app. Note that this isn’t a React class component, but instead a vanilla javascript class. I tried to setup this file as an arrow function but it did not work well. This file is best setup as a class. This file will hold all of our authentication associated functions and variables.

auth.js:这将是应用程序中的唯一类。 请注意,这不是React类的组件,而是普通的javascript类。 我试图将此文件设置为箭头功能,但效果不佳。 最好将此文件设置为类。 该文件将包含我们所有与身份验证相关的功能和变量。

history.js: Will hold the history object which we will use for navigation.

history.js:将保存用于导航的历史记录对象。

authcheck.js: Will be used to update the authentication state of the user and retrieve the user profile data and save it to the global state.

authcheck.js:将用于更新用户的身份验证状态并检索用户配置文件数据并将其保存为全局状态。

主要档案 (Main Files)

These are the main files and will sit at the root /src directory. I put all the business logic for reading and updating the global state in one file, the context_state_config.js. My reason for doing so is follows.

这些是主要文件,位于/ src根目录中 。 我将所有用于读取和更新全局状态的业务逻辑放在一个文件context_state_config.js 。 我这样做的原因如下。

Having all the complexity in one file actually makes your app simpler and easier to debug since it's easy to track down where to make the changes and fixes.

将所有复杂性放在一个文件中实际上使您的应用程序更简单,更容易调试,因为它很容易跟踪更改和修复的位置。

Having many slightly complex components in my experience will actually make your app harder to debug and change. So for this reason I put all the global state code in this one file.

根据我的经验,拥有许多稍微复杂的组件实际上会使您的应用更难以调试和更改。 因此,出于这个原因,我将所有全局状态代码放入此文件中。

Also in the context_state_config.js the <Routes /> component will be wrapped by the <Context.Provider />. This will allow the ability to read and update state to be passed down through the value prop to all the components, creating a global state.

同样在context_state_config.js<Routes />组件将由<Context.Provider />包装。 这将允许通过value道具向下传递和读取状态的能力传递给所有组件,从而创建全局状态。

context_state_config.js: This will hold all the logic for reading and updating the global state with the useReducer hook and context.

context_state_config.js:这将保留使用useReducer钩子和context读取和更新全局状态的所有逻辑。

routes.js: Will contain all our routing logic and will have silent authentication here as well.

route.js:将包含我们所有的路由逻辑,并在此处也具有静默身份验证。

App.js: Our root component, we will simply import and display our context_state_config.js component.

App.js:我们的根组件,我们将简单地导入并显示我们的context_state_config.js组件。

index.js: Our root file, will just render App.js here.

index.js:我们的根文件将在此处呈现App.js。

useRedux与useState的全局上下文状态 (useRedux vs useState for Global Context state)

To manage global state we will be using Reducers and Actions. Using Reducers and Actions along with the useReducer() hook and Context will allow us to achieve Redux like functionality without actually using Redux.

为了管理全局状态,我们将使用Reducers和Actions 。 结合使用useReducer()和Actions以及useReducer()钩子和Context将使我们无需实际使用Redux就可以实现类似Redux的功能。

It is possible  to manage our global state with the useState hook and Context, but using useReducer makes managing global state much more organized. The useState hook is far better at handling local component state.

可以使用useState钩子和Context管理我们的全局状态,但是使用useReducer可以更加有组织地管理全局状态。 useState挂钩在处理本地组件状态方面要好得多。

Having related properties of state and all the update state functions in the same useReducer hook makes things very simple and compartmentalized compared to using the useState  hook which can be much more decentralized.  

与使用useState钩子相比,在同一个useReducer钩子中具有状态的相关属性和所有更新状态函数,使事情变得非常简单和分隔化。

Dispatching actions also makes the data flow easier to follow compared to using the setState function from useState since each action will describe exactly how the state will be changed.

与使用useStatesetState函数相比, 分派操作还使数据流更易于遵循,因为每个操作将准确描述状态的更改方式。

We also dont need to use the combine reducer function or combine our reducers in anyway. Each reducer will be passed into its own useReducer hook.

我们也不需要使用组合减速器功能或以任何方式组合我们的减速器。 每个减速器将传递到其自己的useReducer钩子中。

通过上下文建立全局状态 (Setting up the Global State with Context)

We can begin by setting up the global state, which in my opinion makes it much easier to build the React Components.

我们可以从设置全局状态开始,我认为这使构建React组件变得更加容易。

If you already setup the global state you can build out the component in a very straightforward way instead having to go back and forth between setting up the component and then setting up its state along with it.

如果已经设置了全局状态,则可以以非常直接的方式构建组件,而不必在设置组件与设置状态之间来回移动。

To setup the global state we will need to create our actions, reducers and context.

要设置全局状态,我们将需要创建我们的动作,缩减器和上下文。

Let’s start with our actions types:

让我们从动作类型开始:

//action_types.js

export const SUCCESS = "SUCCESS"

export const FAILURE = "FAILURE"

export const LOGIN_SUCCESS = "LOGIN_SUCCESS"

export const LOGIN_FAILURE = "LOGIN_FAILURE"

export const ADD_PROFILE = "ADD_PROFILE"

export const REMOVE_PROFILE = "REMOVE_PROFILE"

export const USER_INPUT_CHANGE = "USER_INPUT_CHANGE"

export const USER_INPUT_SUBMIT = "USER_INPUT_SUBMIT"

SUCCESS and FAILURE: Will be used as our boiler plate actions.

成功失败:将用作我们的样板动作。

LOGIN_SUCCESS and LOGIN_FAILURE: Used to update authentication state of the user. LOGIN_SUCCESS and LOGOUT_SUCCESS will also work here but I like the dichotomy of success and failure.

LOGIN_SUCCESSLOGIN_FAILURE :用于更新用户的身份验证状态。 LOGIN_SUCCESS和LOGOUT_SUCCESS也可以在这里工作,但我喜欢成功与失败的二分法。

ADD_PROFILE and REMOVE_PROFILE: Used to save the profile data from Auth0 to the global state.

ADD_PROFILEREMOVE_PROFILE:用于将配置文件数据从Auth0保存到全局状态。

USER_INPUT_CHANGE and USER_INPUT_SUBMIT: Used to track the changes and submit of the user submitted text of the form.

USER_INPUT_CHANGEUSER_INPUT_SUBMIT:用于跟踪更改和提交用户提交的表单文本。

动作: (Actions:)

//actions.js

import * as ACTION_TYPES from './action_types'

export const SUCCESS = {
  type: ACTION_TYPES.SUCCESS
}

export const FAILURE = {
  type: ACTION_TYPES.FAILURE
}


export const success = () => {
  return {
    type: ACTION_TYPES.SUCCESS
  }
}

export const failure = () => {
  return {
    type: ACTION_TYPES.FAILURE
  }
}



export const login_success = () => {
  return {
    type: ACTION_TYPES.LOGIN_SUCCESS
  }
}

export const login_failure = () => {
  return {
    type: ACTION_TYPES.LOGIN_FAILURE
  }
}


export const add_profile = (profile) => {
  return {
    type: ACTION_TYPES.ADD_PROFILE,
    payload: profile
  }
}

export const remove_profile = () => {
  return {
    type: ACTION_TYPES.REMOVE_PROFILE
  }
}

export const user_input_change = (text) => {
  return {
    type: ACTION_TYPES.USER_INPUT_CHANGE,
    payload: text
  }
}

export const user_input_submit = (text) => {
  return {
    type: ACTION_TYPES.USER_INPUT_SUBMIT,
    payload: text
  }
}

To keep things simple I have made all the actions into action creators instead of having some as actions and some as action creators.

为了简单起见,我将所有动作都设为动作创建者,而不是将某些动作作为动作创建者,而将某些动作作为动作创建者。

The first 2 variables SUCCESS and FAILURE are regular actions.

前两个变量SUCCESSFAILURE是常规操作。

Auth Reducer: (Auth Reducer:)

//auth_reducer.js

import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
  is_authenticated: false,
  profile: null
}

export const AuthReducer = (state = initialState, action) => {
    switch(action.type) {
      case ACTION_TYPES.LOGIN_SUCCESS:
        return {
          ...state,
          is_authenticated: true
        }
      case ACTION_TYPES.LOGIN_FAILURE:
        return {
          ...state,
          is_authenticated: false
        }
      case ACTION_TYPES.ADD_PROFILE:
        return {
          ...state,
          profile: action.payload
        }
      case ACTION_TYPES.REMOVE_PROFILE:
        return {
          ...state,
          profile: null
        }
      default:
        return state
    }
}

Here we have our auth_reducer.js that will hold our state properties and associated actions for user authentication status and user profile data.

在这里,我们拥有auth_reducer.js ,它将保存我们的状态属性以及与用户身份验证状态用户配置文件数据相关的操作

Important to note that we are exporting both the reducer and initial state instead of exporting default only the reducer like we do in React Redux.

重要的是要注意,我们正在导出化简器和初始状态,而不是像在React Redux中那样仅导出默认的化简器。

form_reducer: (form_reducer:)

//form_reducer.js

import * as ACTION_TYPES from '../actions/action_types'


export const initialState = {
  user_textChange: '',
  user_textSubmit: ''
}


export const FormReducer = (state, action) => {
    switch(action.type) {
      case ACTION_TYPES.USER_INPUT_CHANGE:
        return {
          ...state,
          user_textChange: action.payload
        }
      case ACTION_TYPES.USER_INPUT_SUBMIT:
        return {
          ...state,
          user_textSubmit: action.payload
        }
      default:
        throw new Error();
    }
}

Here we have 2 properties for a form. Our first property tracks changes to the input element and our second property adds the submitted form to the global state.

在这里,我们有2个属性的形式。 我们的第一个属性跟踪对input元素的更改,第二个属性将提交的表单添加到全局状态。

plain_reducer: (plain_reducer: )

//plain_reducer.js


import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
  stateprop1: false,
  stateprop2: false
}

export const Reducer1 = (state = initialState, action) => {
    switch(action.type) {
      case ACTION_TYPES.SUCCESS:
        return {
          ...state,
          stateprop1: true,
          stateprop2: true
        }
      case ACTION_TYPES.FAILURE:
        return {
          ...state,
          stateprop1: false,
          stateprop2: false
        }
      default:
        throw new Error();
    }
}

Like our SUCCESS and FAILURE actions, this reducer will serve as a boilerplate if we want to create new reducers.

就像我们的SUCCESS和FAILURE操作一样,如果我们要创建新的reducer,则该reducer将作为样板。

设置上下文对象 (Setting up the Context Object)

We have to now initialize our Context object. We can do this in a context.js file in the utils directory.

现在,我们必须初始化Context对象。 我们可以在utils目录中的context.js文件中执行此操作。

import React from 'react';

const Context = React.createContext()

export default Context;

This is all we have to do to initialize our Context variable. We can now use it by importing it to our context_state_config.js file.

这就是初始化Context变量所需要做的全部工作。 现在,我们可以通过将其导入到context_state_config.js文件中来使用它。

具有上下文的全局状态 (Global state with Context)

import React, { useReducer } from 'react';
import Context from './utils/context';
import * as ACTIONS from './store/actions/actions';

import * as Reducer1 from './store/reducers/plain_reducer';
import * as AuthReducer from './store/reducers/auth_reducer';
import * as FormReducer from './store/reducers/form_reducer';
import Routes from './routes';

import Auth from './utils/auth';


const auth = new Auth()


const ContextState = () => {
    /*
        Plain Reducer
    */
    const [stateReducer1, dispatchReducer1] = useReducer(Reducer1.Reducer1,
                                                         Reducer1.initialState)


    const handleDispatchTrue = () => {
      //    dispatchReducer1(type: "SUCCESS")
      //    dispatchReducer1(ACTIONS.SUCCESS)
      dispatchReducer1(ACTIONS.success())
    }

    const handleDispatchFalse = () => {
      //     dispatchReducer1(type: "FAILURE")
      //    dispatchReducer1(ACTIONS.FAILURE)
      dispatchReducer1(ACTIONS.failure())
    }

    /*
      Auth Reducer
    */
    const [stateAuthReducer, dispatchAuthReducer] =                      useReducer(AuthReducer.AuthReducer,
                                                           AuthReducer.initialState)


    const handleLogin = () => {
      dispatchAuthReducer(ACTIONS.login_success())
    }

    const handleLogout = () => {
      dispatchAuthReducer(ACTIONS.login_failure())
    }

    const handleAddProfile = (profile) => {
      dispatchAuthReducer(ACTIONS.add_profile(profile))
    }

    const handleRemoveProfile = () => {
      dispatchAuthReducer(ACTIONS.remove_profile())
    }



    /*
      Form Reducer
    */

        const [stateFormReducer, dispatchFormReducer] = useReducer(FormReducer.FormReducer, FormReducer.initialState)


    const handleFormChange = (event) => {
      dispatchFormReducer(ACTIONS.user_input_change(event.target.value))
    };

    const handleFormSubmit = (event) => {
      event.preventDefault();
      event.persist();             dispatchFormReducer(ACTIONS.user_input_submit(event.target.useContext.value))
    };

    //Handle authentication from callback
    const handleAuthentication = (props) => {
      if(props.location.hash) {
        auth.handleAuth()
      }
    }



    return(
      <div>
      <Context.Provider
          value={{
            //Reducer1
            stateProp1: stateReducer1.stateprop1,
            stateProp2: stateReducer1.stateprop2,
            dispatchContextTrue: () => handleDispatchTrue(),
            dispatchContextFalse: () => handleDispatchFalse(),

            //Form Reducer
            useContextChangeState: stateFormReducer.user_textChange,
            useContextSubmitState: stateFormReducer.user_textSubmit,
            useContextSubmit: (event) => handleFormSubmit(event),
            useContextChange: (event) => handleFormChange(event),

            //Auth Reducer
            authState: stateAuthReducer.is_authenticated,
            profileState:  stateAuthReducer.profile,
            handleUserLogin: () => handleLogin(),
            handleUserLogout: () => handleLogout(),
            handleUserAddProfile: (profile) => handleAddProfile(profile),
            handleUserRemoveProfile: () => handleRemoveProfile(),

            //Handle auth
            handleAuth: (props) => handleAuthentication(props),
            authObj: auth
          }}>
          <Routes />
      </Context.Provider>
      </div>
    )
}


export default ContextState;
* Note you probably dont want to have so many variables and functions in context in a real app, this is just for demonstration purposes. Simply remove the properties you don't need.
*请注意,您可能不希望在实际应用中的上下文中具有这么多的变量和函数,这只是出于演示目的。 只需删除不需要的属性。

**Note: You can also use object destructuring on the properties inside the value prop to make the code a little cleaner. Ex:  { handlelogin } instead of handleUserLogin: () => handleLogin(). But I have kept them separate so it will be easier to see how context properties are accessed in child components for people not familiar with destructuring.

**注意:您还可以在value属性内的属性上使用对象分解来使代码更简洁。 例如: { handlelogin }代替handleUserLogin: () => handleLogin() 。 但是我将它们分开放置,因此对于不熟悉解构的人来说,更容易看到如何在子组件中访问上下文属性。

导入reducers和useReducer() (Importing reducers and useReducer())

I will explain how this works using Reducer1 as the example but this applies to the other reducers as well.

我将以Reducer1为例来说明它是如何工作的,但这也适用于其他reducer。

We first start at the very top by importing all our actions and reducers. We then pass in our Reducer1 and its initial state to the useReducer() hook. We use the syntax import * as Reducer1 because we want to import both the Reducer1 and the initialState. Then we use the syntax Reducer1.Reducer1 to access Reducer1 and the intialState can be accessed using Reducer1.initailState.

我们首先从顶部开始,导入所有的动作和减速器。 然后,我们将Reducer1及其初始状态传递给useReducer()挂钩。 我们将语法import * as Reducer1因为我们想同时导入Reducer1initialState 。 然后,我们使用的语法Reducer1.Reducer1访问Reducer1intialState可以使用访问Reducer1.initailState

After that we save the the result of the useReducer() hook using array destructuring.

之后,我们使用数组解构来保存useReducer()挂钩的结果。

In the example above, stateReducer1 is how we access the state properties we defined in the intialState of Reducer1.

在上面的示例中, stateReducer1是我们访问在intialStateReducer1定义的状态属性的Reducer1

dispatchReducer1 is our dispatch function that allows us to update the state with actions.

dispatchReducer1是我们的调度功能,它使我们可以通过操作来更新状态。

减速器命名方案 (Reducer naming scheme)

As you can probably tell, my preferred naming scheme are the words “state” and “dispatch” followed by the name of their respective reducer.

您可能会说,我首选的命名方案是“状态”和“调度”两个词,后跟它们各自的化简器的名称。

I found this to be the most effective naming scheme because it has no ambiguity about which state and dispatch function belongs to which reducer, which is important because we are not combining reducers.

我发现这是最有效的命名方案,因为它对哪个状态和调度功能属于哪个reducer并没有歧义,这很重要,因为我们没有合并reducer。

动作 (Actions)

Our actions are coming from the same actions file we setup in the last section. We import them all here and can access each action with the syntax ACTIONS.name_of_action().

我们的操作来自上一节中设置的相同操作文件。 我们将它们全部导入此处,并可以使用ACTIONS.name_of_action()语法访问每个动作。

This is what we pass into our dispatch function, which tells our reducer how to update the state.  

这就是我们传递给dispatch函数的内容,该函数告诉我们的减速器如何更新状态。

After our useReducer() hook call we have our handleDispatchTrue() and handleDispatchFalse() functions which dispatch our SUCCESS and FAILURE actions to change the our stateprop1 and stateprop2 from false to true and vice versa.

useReducer()挂钩调用之后,我们有了handleDispatchTrue()handleDispatchFalse()函数,这些函数分派了SUCCESSFAILURE操作,以将stateprop1stateprop2从false更改为true,反之亦然。

You can pass in the dispatch functions directly into the “value” prop but having them in a function right under their respective useReducer hook makes the code more organized and readable.

您可以将分派函数直接传递到“值” useReducer但是将它们分别useReducer相应的useReducer钩子下面的函数中,可以使代码更具组织性和可读性。

I have also left 2 other ways of dispatching actions. All three ways of dispatching actions are doing the same thing, dispatching a javascript object with a type property that has a value of the string “SUCCESS”.

我还剩下了另外两种调度动作的方法。 调度动作的所有三种方式都在做相同的事情,即调度具有类型属性的javascript对象,该属性的值为字符串“ SUCCESS”。

AuthReducer (AuthReducer)

Next we have our AuthReducer. We have set this up similar to the plain reducer. We update our user authentication state if they are logged in or not and also add and remove their user profile data from the global state. Remember to pass in the profile parameter to the action creator.

接下来,我们有我们的AuthReducer 。 我们已经将其设置为类似于普通减速器。 如果用户身份验证状态已登录或未登录,我们将对其进行更新,并从全局状态中添加和删除其用户个人资料数据。 请记住将profile参数传递给动作创建者。

FormReducer (FormReducer)

After this we have our FormReducer, which will also be setup similar to the previous reducers.

之后,我们有了FormReducer ,它的设置也将与以前的reducers相似。

Since these actions are going to be used with a form we need to pass in the event keyword as a parameter to both our functions. To access the text our user enters, we need to use the syntax event.target.value. This is part of vanilla javascript and the standard way to access form data.

由于这些动作将与表单一起使用,因此我们需要将event关键字作为参数传递给两个函数。 要访问用户输入的文本,我们需要使用语法event.target.value 。 这是原始javascript的一部分,也是访问表单数据的标准方法。

Our handleFormSubmit() function is a little bit different. First we have to use the event.preventDefault() function to prevent the page from reloading.

我们的handleFormSubmit()函数有些不同。 首先,我们必须使用event.preventDefault()函数来防止重新加载页面。

Then we use the event.persist() function. Since we are using Context and this data is coming from a child component, we have to use this function for the form to function properly. Then to access the user submitted text we use the syntax event.target.useContext.value

然后,我们使用event.persist()函数。 由于我们使用的是Context并且此数据来自子组件,因此我们必须使用此函数使表单正常运行。 然后,使用语法event.target.useContext.value来访问用户提交的文本

"useContext" is not referring to the hook, it is the user defined id property supplied to the form input element. I decided to name the id “useContext” because the component has 2 other forms as well and they use the "useState" and "useReducer" hooks to save the state and therefore have the id of “useState” and “useReducer”.

“ useContext”不是指该钩子,它是提供给表单输入元素的用户定义的id属性。 我决定将ID命名为“ useContext”,因为该组件还有其他两种形式,并且它们使用“ useState”和“ useReducer”钩子保存状态,因此具有“ useState”和“ useReducer”的ID。

上下文提供者 (Context Provider)

After setting up the useReducer hooks we have our <Context.Provider /> component in the JSX. We now pass in all the functions and state values we just defined to the value prop.

设置好useReducer挂钩之后,我们在JSX中有了<Context.Provider />组件。 现在,我们将刚刚定义的所有函数和状态值传递给value prop。

We start with stateprop1 and stateprop2. Important to note that they each have to be accessed using dot notation separately since stateReducer1 contains the entire initialState object.

我们从stateprop1stateprop2开始。 重要的是要注意,由于stateReducer1包含整个initialState对象,因此必须分别使用点符号对其进行访问。

We also define 2 other properties, dispatchContextTrue and dispatchContextFalse, and pass in an arrow function for each that calls our handleDispatchTrue() and handleDispatchFalse() functions. It might be helpful for you to name the properties different than the function names. This helps you better see whats happening in child components.

我们还定义了其他两个属性dispatchContextTruedispatchContextFalse ,并为每个调用handleDispatchTrue()handleDispatchFalse()函数的箭头函数传递一个箭头函数。 命名不同于函数名称的属性可能对您有所帮助。 这可以帮助您更好地了解子组件中发生的情况。

Next we will continue building our app by setting up authentication.

接下来,我们将通过设置身份验证来继续构建我们的应用程序。

身份验证和Authcheck (Authentication and Authcheck)

Here we have our auth.js file which will be setup as a Javascript class. And we will be using Auth0 and the auth0-js library to help us with authentication.

在这里,我们有auth.js文件,它将被设置为Javascript类。 我们将使用Auth0和auth0-js库来帮助我们进行身份验证。

The authentication utility file will be setup as follows:

身份验证实用程序文件将如下设置:

import auth0 from 'auth0-js'
import history from './history';

export default class Auth {
  auth0 = new auth0.WebAuth({
    domain: 'webapp1.auth0.com',
    clientID: '',
    redirectUri: 'http://localhost:3000/callback',
    responseType: 'token id_token',
    scope: 'openid profile email'
  })

  userProfile = {}

  login = () => {
      this.auth0.authorize()
  }

  handleAuth = () => {
    this.auth0.parseHash((err, authResult) => {
      if(authResult) {
        localStorage.setItem('access_token', authResult.accessToken)
        localStorage.setItem('id_token', authResult.idToken)

        let expiresAt = JSON.stringify((authResult.expiresIn * 1000 + new Date().getTime()))
        localStorage.setItem('expiresAt', expiresAt)

        this.getProfile();
        setTimeout(() => { history.replace('/authcheck') }, 600);
      } else {
        console.log(err)
      }
    })
  }

  getAccessToken = () => {
    if(localStorage.getItem('access_token')) {
      const accessToken = localStorage.getItem('access_token')
      return accessToken
    } else {
      return null
    }
  }


  getProfile = () => {
    let accessToken = this.getAccessToken()
    if(accessToken) {
      this.auth0.client.userInfo(accessToken, (err, profile) => {
          if(profile) {
            this.userProfile = { profile }
          }
      } )
    }
  }


  logout = () => {
    localStorage.removeItem('access_token')
    localStorage.removeItem('id_token')
    localStorage.removeItem('expiresAt')
    setTimeout(() => { history.replace('/authcheck') }, 200);
  }

  isAuthenticated = () => {
    let expiresAt = JSON.parse(localStorage.getItem('expiresAt'))
    return new Date().getTime() < expiresAt
  }

}

auth0: This is the property we will use to initialize our Auth0 app.

auth0 这是我们将用来初始化Auth0应用程序的属性。

userProfile: This is an empty object that will hold the user profile data we get from Auth0.

userProfile 这是一个空对象,将保存我们从Auth0获得的用户个人资料数据。

login: This brings up the Auth0 login widget, allowing the user to login with the given .authorize() function.  

login :这将显示Auth0登录小部件,允许用户使用给定的.authorize()函数进行登录。

handleAuth: This function saves the id and access tokens we get from Auth0 to the local browser storage. This function also sets the token expires time.

handleAuth :此函数将我们从Auth0获得的ID和访问令牌保存到本地浏览器存储中。 此功能还设置令牌的过期时间。

getAccessToken:  Get the access token from local storage

getAccessToken 从本地存储获取访问令牌

getProfile: Parse the access token to extract the user profile data

getProfile :解析访问令牌以提取用户配置文件数据

logout: Logs out the user by removing the tokens from local storage

logout 通过从本地存储中删除令牌来logout用户

isAuthenticated: makes sure the user is logged in by comparing the expires time to the current time.

isAuthenticated 通过将过期时间与当前时间进行比较,确保用户已登录。

Now we can initialize this auth object and add authentication to the context_state_config.js file.

现在,我们可以初始化此身份验证对象,并将身份验证添加到context_state_config.js文件。

....


import Auth from './utils/auth';


const auth = new Auth()


const ContextState = () => {

....


//Handle authentication from callback
    const handleAuthentication = (props) => {
      if(props.location.hash) {
        auth.handleAuth()
      }
    }

....

        //Handle auth
        handleAuth: (props) => handleAuthentication(props),
        authObj: auth
        }}>
       <Routes />
     </Context.Provider>

....

new Auth () is how we initialize our class then save it in the auth variable.

new Auth ()是我们初始化类然后将其保存在auth变量中的方式。

Next we create a handleAuthentication() function. If props.location.hash is true then we call the auth.handleAuth() function we just setup in the Auth class. props.location.hash is a given react-router functionality that checks if there is any value in the URL hash fragment.

接下来,我们创建一个handleAuthentication()函数。 如果props.location.hash为true,则调用我们在Auth类中设置的auth.handleAuth()函数。 props.location.hash是给定的react-router功能,用于检查URL哈希片段中是否有任何值。

If Auth0 successfully authenticates a user, the access and id tokens will be included after a hash in the URL, making props.location.hash true, which calls our handleAuth() function in the Auth class.

如果Auth0成功验证了用户身份,则访问和ID令牌将包含在URL中的哈希之后,使props.location.hash true,这将在Auth类中调用handleAuth()函数。

In the <Context.Provider />  we have 2 properties, handleAuth which calls our handleAuthentication() function and authObj which we use to pass down our entire Auth class and allow all components to access our authentication functions and variables.

<Context.Provider />我们有2个属性, handleAuth调用了handleAuthentication()函数, authObj用来传递整个Auth类,并允许所有组件访问我们的身份验证函数和变量。

Here is our authcheck.js utility component:

这是我们的authcheck.js实用程序组件:

import React, { useEffect, useContext } from 'react';
import history from './history';
import Context from './context';
import * as ACTIONS from '../store/actions/actions';



const AuthCheck = () => {
  const context = useContext(Context)

  useEffect(() => {
    if(context.authObj.isAuthenticated()) {
      context.handleUserLogin()
      context.handleUserAddProfile(context.authObj.userProfile)
      history.replace('/')
    }
    else {
      context.handleUserLogout()
      context.handleUserRemoveProfile()
      history.replace('/')
      }
    }, [])

    return(
        <div>
        </div>
    )}




export default AuthCheck;

This component is essentially how we update the authentication state using the useEffect() hook.

从本质useEffect()此组件是我们使用useEffect()挂钩更新身份验证状态的useEffect()

This component will be rendered every time a user logs in and out. Having one component render after every log in and log out will save us from having to handle and update the context authentication state in every component.

每次用户登录和注销时都会呈现此组件。 每次登录和注销后都有一个组件呈现将使我们免于必须处理和更新每个组件中的上下文身份验证状态。

In our AuthCheck component we first start by setting up the useContext() hook. Then we define a conditional statement to check if the isAuthenticated() function that we setup in the Auth class returns true, indicating the auth tokens in local storage haven't expired and the user is still authenticated.

在我们的AuthCheck组件中,我们首先从设置useContext()挂钩开始。 然后,我们定义一个条件语句,以检查我们在Auth类中设置的isAuthenticated()函数是否返回true,这表明本地存储中的auth令牌尚未过期,并且用户仍通过了身份验证。

And we access that function with the syntax context.authObj.isAuthenticated.

然后,我们使用语法context.authObj.isAuthenticated访问该函数。

And we can do this because we passed the entire Auth class down as a property called authObj to the value prop in Context.

之所以可以这样做,是因为我们将整个Auth类作为一个名为authObj的属性authObj给了Context中的value prop。

If isAuthentciated() is true we call our properties to change our login state to true and save the user profile data to the global state.  

如果isAuthentciated()为true,则调用属性以将登录状态更改为true,并将用户配置文件数据保存为全局状态。

If a user logs out, we do the opposite.

如果用户注销,则相反。

We are returning an empty div since we are just updating the state and dont need to show anything in the UI. A loading screen would be good here but that’s for another tutorial.

我们返回的是一个空div,因为我们只是在更新状态,而无需在UI中显示任何内容。 加载屏幕在这里会很好,但这是另一个教程。

But this is it, we are done setting up our global state and authentication system, we can now set up our React Hooks Components.

就是这样,我们已经完成了设置全局状态和身份验证系统的工作,现在可以设置React Hooks组件了。

React Hooks组件 (React Hooks Components)

First we’ll start with our callback.js component

首先,我们将从callback.js组件开始

import React from 'react'

const Callback = props => (
    <div>
      Callback
    </div>
);

export default Callback;

This component is what the user is redirected to after logging in with Auth0. From here the user is redirected to the authcheck page then the home page

使用Auth0登录后,用户将被重定向到该组件。 从此处将用户重定向到authcheck页面,然后重定向到主页

Header.js (Header.js)

import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import Context from '../utils/context';

const Header = () => {
  const context = useContext(Context)

    return(
        <div>
          <Link to='/' style={{padding: '5px'}}>
            Home
          </Link>
          <Link to='/profile' style={{padding: '5px'}}>
            Profile
          </Link>
          <Link to='/hooksform' style={{padding: '5px'}}>
            Hooks Form
          </Link>
          <Link to='/hookscontainer' style={{padding: '5px'}}>
            Hooks Container
          </Link>
          <Link to='/privateroute' style={{padding: '5px'}}>
            Private Route
          </Link>
          {!context.authState
            ? <button onClick={() => context.authObj.login()}>Login</button>
            : <button onClick={() => context.authObj.logout()}>Logout</button>
          }
        </div>
  )};


export default Header;

Here we have links to all our components. We also have a ternary expression that displays either a login or logout button depending on whether the user is authenticated or not.

在这里,我们有所有组件的链接。 我们还有一个三元表达式,它根据用户是否通过身份验证显示登录或注销按钮。

home.js (home.js)

import React from 'react'

const Home = props => (
    <div>
      Home
    </div>
);

export default Home;

a simple home.js component

一个简单的home.js组件

hooks1.js (hooks1.js)

import React, { useContext, useState, useEffect, useReducer } from 'react';
import * as ACTIONS from '../store/actions/actions';
import * as Reducer1 from '../store/reducers/plain_reducer';
import Context from '../utils/context';


const HooksContainer1 = () => {
  const context = useContext(Context)

  const [value, setValue] = useState(0)

  const [useEffectValue, setUseEffectValue] = useState(null)

  const [state, dispatch] = useReducer(Reducer1.Reducer1, Reducer1.initialState)

  useEffect(() => {
      setTimeout(() => setUseEffectValue("useEffect worked"), 3000 );
  }, [value])

  const incrementValue = () => {
    setValue(value + 1 )
  }

  const decrementValue = () => {
    setValue(value - 1 )
  }

  const handleuseEffectValue = () => {
    setUseEffectValue("some string")
  }

  const handleDispatchTrue = () => {
    //    dispatch2(type: "SUCCESS")
    //    dispatch2(ACTIONS.SUCCESS)
    dispatch(ACTIONS.success())
  }

  const handleDispatchFalse = () => {
    //     dispatch2(type: "FAILURE")
    //    dispatch2(ACTIONS.FAILURE)
    dispatch(ACTIONS.failure())
  }

  return (
    <div>
      <div>
      <button onClick={() => handleuseEffectValue()}> Handle Value  </button>
      <button onClick={() => handleDispatchTrue()}>Dispatch True </button>
      <button onClick={() => handleDispatchFalse()}>Dispatch False </button>
      <button onClick={() => context.dispatchContextTrue()}>Dispatch Context True </button>
      <button onClick={() => context.dispatchContextFalse()}>Dispatch Context False </button>
      <button onClick={() => incrementValue()}> Add Local Value </button>
      <button onClick={() => decrementValue()}> Dec Local Value </button>
      <br />
      <br />
      {context.useContextSubmitState
        ? <h3> {context.useContextSubmitState} </h3>
        : <h3> No User Text </h3>
      }
      <br />
      {state.stateprop1
        ? <p> stateprop1 is true </p>
        : <p> stateprop1 is false </p>
      }
      <br />
      {context.stateProp2
        ? <p> stateprop2 is true </p>
        : <p> stateprop2 is false </p>
      }
      <br />
      {useEffectValue
        ? <p> { useEffectValue }</p>
        : <p> No value </p>
      }
      <br />
      <p>Local Value: {value}</p>
      <br />
      <br />
      </div>
    </div>
  )
}

export default HooksContainer1;

I created this component as a boilerplate to have all the ways to read and update state in one component. This makes it much easier to see the syntax differences.

我创建了该组件作为样板,使其具有在一个组件中读取和更新状态的所有方式。 这使得查看语法差异变得更加容易。

incrementValue and decrementValue is how we update the local state with the useState() hook.

incrementValuedecrementValue是我们使用useState()挂钩更新本地状态的useState()

handleuseEffectValue is how we update the useEffectValue property of local state.

handleuseEffectValue是我们如何更新本地状态的useEffectValue属性。

handleDispatchTrue and handleDispatchFalse is how we dispatch our actions to change our stateprop1 in Reducer1 from true to false, and vice versa. Note that this is still local state even though we are using reducers and actions.

handleDispatchTruehandleDispatchFalse是我们调度动作的方式,以将stateprop1中的Reducer1从true更改为false,反之亦然。 请注意,即使我们正在使用化简器和操作,这仍然是局部状态。

handleContextDispatchTrue and handleContextDispatchFalse is how we update our global state using the same actions and reducer as the handleDispatchTrue and handleDispatchFalse functions.

handleContextDispatchTruehandleContextDispatchFalse是我们如何使用与handleDispatchTruehandleDispatchFalse函数相同的操作和handleDispatchTrue来更新全局状态的方法。

In our JSX we also see that each function has its own button.

在我们的JSX中,我们还看到每个函数都有其自己的按钮。

context.useContextSubmitState is how we display text from a form that saves values to the global state, which we will see next

context.useContextSubmitState是我们如何显示表单中的文本的形式,该表单将值保存到全局状态,接下来我们将看到

state.stateprop1 is the stateprop1 property from the Reducer1 initialState that we set up a while ago and state is the user defined keyword from the useRedcuer hook at the top. The entire initialState is contained in state.

state.stateprop1是我们之前设置的Reducer1 initialStatestateprop1属性,而state是顶部的useRedcuer挂钩中用户定义的关键字。 整个initialState包含在state

context.stateProp2 is the stateprop2 value we are getting from our context global state.

context.stateProp2是从上下文全局状态中获取的stateprop2值。

useEffectValue is the local state from the useState hook call.

useEffectValueuseState挂钩调用中的本地状态。

hooks_form1.js (hooks_form1.js)

Here we have our hooks1_form.js that shows how to save the state from a form using the useReducer, useState and useContext hooks.

在这里,我们有hooks1_form.js ,它显示了如何使用useReduceruseStateuseContext挂钩从表单保存状态。

import React, { useContext, useState, useReducer } from 'react';
import * as ACTIONS from '../store/actions/actions';
import * as FormReducer from '../store/reducers/form_reducer';
import Context from '../utils/context';


const HooksForm1 = () => {
  const context = useContext(Context)

  const [valueChange, setValueChange] = useState('')
  const [valueSubmit, setValueSubmit] = useState('')

  const [state, dispatch] = useReducer(FormReducer.FormReducer,
                                       FormReducer.initialState)


  const handleuseStateChange = (event) => (
    setValueChange(event.target.value)
  );

  const handleuseStateSubmit = (event) => {
    event.preventDefault();
    setValueSubmit(event.target.useState.value)
  };

  const handleuseReducerChange = (event) => (
    dispatch(ACTIONS.user_input_change(event.target.value))
  );

  const handleuseReducerSubmit = (event) => {
    event.preventDefault();
    dispatch(ACTIONS.user_input_submit(event.target.useReducer.value))
  };


    return (
      <div>
        <form onSubmit={handleuseStateSubmit}>
          <label> React useState: </label>
          <input id="useState" onChange={handleuseStateChange} type="text" />
          <button type="submit"> Submit </button>
        </form>
        <br />
        <form onSubmit={handleuseReducerSubmit}>
          <label> React useReducer: </label>
          <input id="useReducer" onChange={handleuseReducerChange} type="text" />
          <button type="submit"> Submit </button>
        </form>
        <br />
        <form onSubmit={context.useContextSubmit}>
          <label> React useContext: </label>
          <input id="useContext" onChange={context.useContextChange} type="text" />
          <button type="submit"> Submit </button>
        </form>
        <br />

        <h3>React useState:</h3>
        <p>Change: {valueChange}</p>
        <p>Submit: {valueSubmit}</p>
        
        <h3>React useReducer:</h3>
        <p>Change: {state.user_textChange}</p>
        <p>Submit: {state.user_textSubmit}</p>
        <br />
        <h3>React useContext:</h3>
        <p>Change: {context.useContextChangeState}</p>
        <p>Submit: {context.useContextSubmitState}</p>
        <br />
        <br />
      </div>
    )
}


export default HooksForm1;

This form shows the three ways to update state and follows the same exact methodology as we saw in the previous component.  

此表单显示了三种更新状态的方法,并遵循与上一部分中所见相同的精确方法。

privatecomponent.js (privatecomponent.js)

import React from 'react'

const PrivateComponent = props => (
    <div>
      Private Component
    </div>
);

export default PrivateComponent;

This privatecomponent will be used in a private route and be only accessible by authenticated users.

该私有组件将在私有路由中使用,并且只有经过身份验证的用户才能访问。

profile.js (profile.js)

import React, { useContext } from 'react';
import Context from '../utils/context';


const Profile = () => {
  const context = useContext(Context)


  const RenderProfile = (props) => {
    return(
      <div>
        <h1>{props.profile.profile.nickname}</h1>
        <br />
        <img src={props.profile.profile.picture} alt="" />
        <br />
        <h4> {props.profile.profile.email}</h4>
        <br />
        <h5> {props.profile.profile.name} </h5>
        <br />
        <h6> Email Verified: </h6>
        {props.profile.profile.email_verified ? <p>Yes</p> : <p>No</p> }
        <br />
      </div>
     )
   }


    return(
      <div>
        <RenderProfile profile={context.authObj.userProfile} />
      </div>
  )}



export default (Profile);

Here we display the user profile data. The user profile data is available from Auth0 and we do not have to set it up manually. We are getting this user profile data from our authObj that we passed down through context.

在这里,我们显示用户个人资料数据。 可以从Auth0获得用户个人资料数据,而不必手动进行设置。 我们是从我们通过context传递的authObj获取此用户配置文件数据的。

路由 (Routing)

Before we can setup our routing we need to first setup the history.js file which is luckily very easy to do.

在设置路由之前,我们需要先设置history.js文件,这很容易做到。

import { createBrowserHistory } from 'history'

export default createBrowserHistory()

Finally we can setup our Routing:

最后,我们可以设置路由:

import React, { useContext, useEffect } from 'react';
import { Router, Route, Switch, Redirect } from 'react-router';
import history from './utils/history';
import Context from './utils/context';
import AuthCheck from './utils/authcheck';

import Home from './hooks/home';
import Header from './hooks/header';
import HooksContainer1 from './hooks/hook1';
import Callback from './hooks/callback';
import HooksForm from './hooks/hooks_form1';
import PrivateComponent from './hooks/privatecomponent';
import Profile from './hooks/profile';



const PrivateRoute = ({component: Component, auth }) => (
  <Route render={props => auth === true
    ? <Component auth={auth} {...props} />
    : <Redirect to={{pathname:'/'}} />
  }
  />
)



const Routes = () => {
    const context = useContext(Context)


      return(
        <div>
          <Router history={history} >
          <Header />
          <br />
          <br />
          <div>
            <Switch>
              <Route exact path='/' component={Home} />
              <Route path='/hooksform' component={HooksForm} />
              <Route path='/profile' component={Profile} />
              <Route path='/hookscontainer' component={HooksContainer1} />
              <Route path='/authcheck' component={AuthCheck} />

              <PrivateRoute path='/privateroute'
                            auth={context.authState}
                            component={PrivateComponent} />
              <PrivateRoute path="/profile"
                            auth={context.authState}
                            component={Profile} />
              <Route path='/callback'
					 render={(props) => {
                         context.handleAuth(props);                                                            return <Callback />}} />


            </Switch>
          </div>
          </Router>
        </div>
  )}

export default Routes;

We first start by importing all our utility files and components. And also the Router components from React Router.

首先,我们先导入所有实用程序文件和组件。 还有来自React Router的Router组件。

We then have a PrivateRoute Higher Order Component that’s going to be responsible for our private routes.

然后,我们有一个PrivateRoute高阶组件,它将负责我们的私有路线。

A HOC takes in a component and returns another component. Here we are passing in a component and either returning a <Route /> component or a <Redirect /> component based on the user authentication state. We check for the auth state inside our our render prop with a ternary expression.  

HOC接受一个组件并返回另一个组件。 在这里,我们传入一个组件,并根据用户身份验证状态返回<Route />组件或<Redirect />组件。 我们使用三元表达式检查render道具内部的auth状态。

Next we have our actual Router functionality. We will start with our main <Router /> component which will wrap all of our routes and header.

接下来,我们有实际的路由器功能。 我们将从主要的<Router />组件开始,它将包装我们的所有路由和标头。

We always want the header to be showing so we will of course put it outside of the <Switch /> component. Our <Switch /> component will then wrap all of our routes. We can define the routes and components using the path and component props of the <Route /> component.

我们始终希望显示标题,因此我们当然会将其放在<Switch />组件之外。 然后,我们的<Switch />组件将包装我们的所有路线。 我们可以使用<Route />组件的pathcomponent属性来定义路由和组件。

Our <PrivateRoute /> component is a little bit different. We have to specify the path and component props like we did for the regular <Route />, but we also have to create a auth prop that contains the authentication state of the user. We get this value from our global context state that we went over in the authentication section, but basically this auth prop contains the value of the is_authenticated property from our AuthReducer from the global state.

我们的<PrivateRoute />组件有些不同。 我们必须像常规<Route />一样指定pathcomponent属性,但是我们还必须创建一个包含用户身份验证状态的身份auth属性。 我们从认证部分中遍历的全局上下文状态获得此值,但基本上,该auth属性包含来自AuthReducer全局状态的is_authenticated属性的值。

Finally we have our /callback route which is setup a little bit different. Since this is the component that Auth0 redirects to, we have to call the handleAuth()  function here, but we also have to render the <Callback /> component.

最后,我们有/callback路由,它的设置有些不同。 由于这是Auth0重定向到的组件,因此我们必须在这里调用handleAuth()函数,但是我们还必须呈现<Callback />组件。

We get around this by calling 2 functions in the render prop, which we can do by wrapping the body of the arrow function in curly brackets {} and separating each function with a semi-colon ;.

我们通过在render prop中调用2个函数来解决这个问题,我们可以通过将箭头函数的主体括在大括号{}并用分号分隔每个函数来实现;

Also be sure to wrap all the routes with the <Context.Provider />

另外,请务必使用<Context.Provider />包装所有路由

//context_state_config.js
...    
     <Context.Provider>
    	 <Routes />
     </Context.Provider>
      
 ...

Wrapping all the routes with the <Context.Provider /> is essentially how state gets passed down to all the components, and becomes global.

<Context.Provider />包装所有路由本质上是将状态传递给所有组件并变为全局的方式。

App.js (App.js)

import React from 'react';
import ContextState from './context_state_config';

const App = () => {

    return(
      <div>
     	 <ContextState />
      </div>
    )
}

export default App;

Now the only thing we have left to do is import our <ContextState /> component to our App.js file to finish off our app.

现在我们唯一要做的就是将<ContextState />组件导入到App.js文件中,以完成我们的应用程序。

And we are done! Thanks for reading.

我们完成了! 谢谢阅读。

Connect with me on Twitter for more updates on future tutorials: https://twitter.com/iqbal125sf

在Twitter上与我联系以获取未来教程的更多更新: https//twitter.com/iqbal125sf

翻译自: https://www.freecodecamp.org/news/build-a-react-hooks-front-end-app-with-routing-and-authentication/

前端 路由权限react

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值