如何使用React,Redux和Redux Saga设置用户身份验证

by Zafar Saleem

通过Zafar Saleem

UPDATE(12.02.2019): I recently updated this project with most recent react routers i.e. version 4.3.1 which is react-router-dom. Please head to its repository to view the changes.

更新(12.02.2019):我最近用最新的React Router更新了这个项目,即4.3.1版本是react-router-dom。 请转到其存储库以查看更改。

In my previous blog I wrote how to write a scalable architecture in Node.js. Since I used postman to test the working of that platform, I thought this would be a good idea to have its client side implementation. To write its client side I decided to use the tech stack below:

在我以前的博客中,我写了如何在Node.js中编写可伸缩的体系结构。 由于我使用邮递员来测试该平台的工作,因此我认为拥有其客户端实现是一个好主意。 为了编写其客户端,我决定使用以下技术堆栈:

  • React

    React
  • Redux

    Redux
  • Redux-Saga

    Redux-Saga
  • React Router

    React路由器

This post assumes that you already know react and basic concepts of Redux and Redux-Saga.

这篇文章假设您已经了解Redux和Redux-Saga的React和基本概念。

入门 (Getting started)

Clone my previous blog repository. CD into its root folder and run npm install. This will install all dependencies.

克隆我以前的博客CD放入其根文件夹并运行npm install 这将安装所有依赖项。

Secondly, install mongodb in your machine. Once installed run mongo server using the mongod command in your terminal, if it is not started as a service in your machine.

其次, 安装mongodb 在您的机器上。 安装后,使用mongod运行mongo服务器 如果您的终端不是作为计算机中的服务启动的,请在终端中使用该命令。

Next, make sure the nodemon package is installed on your machine globally. Go to the server side folder and run nodemon index.js to run the backend server.

接下来,确保nodemon软件包已全局安装在您的计算机上。 转到服务器端文件夹并运行nodemon index.js 运行后端服务器。

Now that our backend is up and running, it’s time to get into its client side implementation.

现在我们的后端已经启动并正在运行,是时候进入其客户端实现了。

If you haven’t yet installed create-react-app then go ahead install it using following command.

如果尚未安装create-react-app 然后继续使用以下命令进行安装。

npm install create-react-app -g

This command will install create-react-app globally.

此命令将安装create-react-app 全球范围

创建项目 (Create the project)

Now it’s time to create a project. Use:

现在是时候创建一个项目了。 用:

create-react-app react-login

This will create a new project with the name react-login. Go ahead and cd into that folder. Open your package.json file in your favourite editor and add following dependencies:

这将创建一个名为react-login的新项目 继续并cd进入该文件夹。 打开你的package.json 文件在您喜欢的编辑器中,并添加以下依赖项:

We don’t need any additional properties in this package.json file. We can simply remove them, but I’ll leave it as is and move forward so that we get to interesting part in this blog.

在这个package.json文件中,我们不需要任何其他属性。 我们可以简单地删除它们,但是我将其保留原样并继续前进,以便使我们进入此博客中有趣的部分。

Now simply run:

现在只需运行:

npm install

which will install all the dependencies we mentioned above.

它将安装我们上面提到的所有依赖项。

索引文件 (Index file)

To start with, open the index.js file and place the code below into this file.

首先,打开index.js 文件,并将下面的代码放入该文件。

In this code we are importing react and react-dom. Then we import Router and browserHistory from react-router. These are required for routing purposes, which I will be using later in the routes/index.js file. Next, we import Provider, this is used to provide store to child components.

在此代码中,我们将导入reactreact-dom 。 然后我们从react-router导入RouterbrowserHistory 。 这些是路由目的所必需的,稍后将在routes/index.js 文件。 接下来,我们导入Provider ,它用于为子组件提供存储。

configureStore and routes are something we are going to import next and which I will implement in a second. Just import them as is and use them in this file as shown above.

configureStoreroutes是我们接下来要导入的内容,我将在稍后实现。 只需按原样导入它们,并在此文件中使用它们即可,如上所示。

Now our index file is set up.

现在我们的索引文件已经建立。

商店配置 (Store configuration)

Create a new folder called store inside the src folder. Inside that new folder, create a file called configureStore.js, and paste following code into that file.

创建一个名为store的新文件夹 src内部 夹。 在该新文件夹中,创建一个名为configureStore.js的文件, 并将以下代码粘贴到该文件中。

First we are importing createStore, which will be used to createStore, and applyMiddleware, which will be used apply middlewares to our store — sagas in this case, but we will get into that later in this blog — from redux.

首先,我们从redux导入createStore (将用于createStore )和applyMiddleware (将用于将中间件应用于我们的商店)(在这种情况下为sagas,但我们将在本博客的后面部分介绍)。

We then import rootReducer — we are going to create this later. For now, simply import it and use it as is. This is followed by the function configureStore , which returns an object by calling the createStore function and passing rootReducer as the parameter.

然后,我们导入rootReducer我们将在以后创建它。 现在,只需将其导入并按原样使用即可。 其后是函数configureStore ,该函数通过调用createStore函数并将rootReducer作为参数来返回对象。

Finally, export configureStore makes configureStore available in the index.js file, constructed earlier.

最后, export configureStore使configureStore在先前构建的index.js文件中可用。

Now that is out of our way, go ahead and create the src/reducers folder, create index.js file and paste the code below in this file.

现在已经不可行了,继续创建src/reducers 文件夹,创建index.js文件,并将下面的代码粘贴到该文件中。

This file is responsible for importing the rest of the reducers inside the reducers folder, combining them, and export them so that they are available to be used in configureStore.js. We will make changes to this file when we add new reducers later in this blog.

该文件负责将其余的reducers导入reducers文件夹中,进行合并,然后导出它们,以便可在configureStore.js 。 当我们稍后在此博客中添加新的reducer时,我们将对该文件进行更改。

路由文件 (Routing file)

Time for the routes file. Go ahead and create the src/routes folder and inside this folder create an index.js file. Now open it and paste in the below code.

路由文件的时间。 继续创建src/routes 文件夹并在此文件夹内创建一个index.js 文件。 现在将其打开并粘贴以下代码。

The main goal of this file is to handle routing in our project. The file imports React, Route and IndexRoute. After that, we need a container, in this case I am importing container/App, which we are going to write soon. Next is RegisterPage, which is a component, and we will write that as well.

该文件的主要目标是处理我们项目中的路由。 该文件导入ReactRouteIndexRoute 。 之后,我们需要一个容器,在这种情况下,我将导入container/App ,我们将很快编写它。 接下来是RegisterPage ,它是一个组件,我们也将编写它。

In the parent Route, when the home path matches then we simply render our App container. On IndexRoute users will see RegisterPage which will be rendered inside the App container.

在父Route ,当主路径匹配时,我们只需渲染App容器。 在IndexRoute用户将看到RegisterPage ,它将在App容器内呈现。

容器 (Container)

Now it’s time for the container. Go ahead and make a new folder called container. Inside this folder create a new file called App.js and place the below code into this file.

现在是该容器的时候了。 继续创建一个名为container的新文件夹。 在此文件夹中,创建一个名为App.js的新文件,并将以下代码放入该文件中。

This is pretty straightforward. The main purpose of this file is to render the rest of the components. {this.props.children} serves this purpose.

这很简单。 该文件的主要目的是渲染其余组件。 {this.props.children} 达到这个目的。

注册 (Registration)

Now it is time for registerPage. Create a new folder src/components and create a component inside the components folder called registerPage.js. Paste the below code into this component.

现在是时候registerPage 。 创建一个新的文件夹src/components 并在components文件夹中创建一个名为 registerPage.js 。 将以下代码粘贴到该组件中。

For now, this is a very simple component. We will edit this later to add a registration form and put some functionality into it.

目前,这是一个非常简单的组件。 稍后我们将对其进行编辑以添加注册表并在其中添加一些功能。

输出量 (Output)

After creating all the folders and files above, run npm start in your project, and open http://localhost:3000 in your browser. You should be able to see the below result.

在创建完以上所有文件夹和文件后,在项目中运行npm start ,然后打开http://localhost:3000 在您的浏览器中。 您应该能够看到以下结果。

Clicking on login here will not redirect to the login route which we will fix next.

单击此处的登录名将不会重定向到下一步将修复的登录路径。

使它工作 (Making it work)

路由 (Routing)

For routing to work, first make a new component inside the components folder. Name it loginPage.js and place the below code inside this component.

要进行路由工作,请首先在components文件夹中制作一个新组件。 将其命名为loginPage.js并将以下代码放入该组件中。

This component is very simple. It renders basic content and a link to register the component.

这个组件非常简单。 它呈现基本内容以及用于注册组件的链接。

Now open the routes.js file, which we already created above, and make following changes.

现在打开上面已经创建的routes.js文件,并进行以下更改。

Change the index route to LoginPage because we want users to land on the login component when they visit the home page. Before doing that, import it from components folder.

将索引路由更改为LoginPage因为我们希望用户在访问主页时能够登录到登录组件。 在此之前,请从components文件夹导入它。

Now refresh your browser and you should be able to see loginPage first. When you click on the “Register here” link, registerPage should be rendered.

现在刷新您的浏览器,您应该能够首先看到loginPage 。 当您单击“在此处注册”链接时,应该呈现registerPage

Now we have the basic routes working.

现在我们有了基本路线。

登录和注册 (Login and registration)

注册 (Registration)

In order to make the login process work, I will first handle the registration process so that we add some users in our database. So let’s go ahead and open components/registerPage.js and update it with the below contents.

为了使登录过程正常进行,我将首先处理注册过程,以便在数据库中添加一些用户。 因此,让我们继续打开components/registerPage.js ,并使用以下内容对其进行更新。

There seems to be a lot of code in this file now, but it’s all simple. First we are importing connect to connect our store with the registerPage component. Then we import registerUserAction which we will write next.

现在此文件中似乎有很多代码,但这很简单。 首先我们要导入connect 将我们的storeregisterPage连接 零件。 然后我们导入registerUserAction 接下来我们要写。

Inside the render function, first I am checking the response from the server if it exists, then assigning success and message properties that are received from the server. This can be a separate function but, for simplicity’s sake, I placed them in the render function.

render函数内部,首先我要检查来自服务器的响应(如果存在),然后分配从服务器接收到的成功和消息属性。 这可以是一个单独的函数,但是为了简单起见,我将它们放在了render函数中。

Next there is a registration form. When user clicks on the register button it triggers the onHandleRegistration function which gets the user’s entered data from the form, and dispatch registerUserAction with their data as parameters. We are going to write actions in the next step.

接下来是注册表。 当用户单击注册按钮时,它将触发onHandleRegistration函数,该函数从表单获取用户输入的数据,并dispatch registerUserAction 以他们的数据作为参数。 我们将在下一步中编写动作。

In order for the above code to work, we need mapStateToProps, as we are doing at the bottom of the component, and then connect it with the registerPage component at the end.

为了使上面的代码起作用,我们需要mapStateToProps ,就像我们在组件底部所做的那样,然后在最后将它与registerPage组件连接。

Actions

动作

Now it’s time to add actions. Go ahead and create the src/actions folder. Create the index.js file and place the below code in it.

现在是时候添加动作了。 继续创建src/actions 夹。 创建index.js 文件,并将以下代码放入其中。

This code exports some constants that we will be using throughout our project.

此代码导出了将在整个项目中使用的一些常量。

Now go ahead and create the authenticationActions.js file inside the same folder, and place the below code in it.

现在继续创建authenticationActions.js 文件放在同一文件夹中,然后将以下代码放入其中。

Here I am importing the index file, which exports constants, and then I export registrationUserAction and return an object with action type and user data. Action type in this case is REGISTER_USER. This action will be dispatched when a user is trying to register, and this action will be available throughout our project which we will listen to in our sagas.

在这里,我将导入索引文件,该文件将导出常量,然后export registrationUserAction并返回具有操作类型和用户数据的对象。 在这种情况下,操作类型为REGISTER_USER 。 当用户尝试注册时,将分派该操作,并且该操作将在我们的整个项目中可用,我们将在saga中收听。

Sagas

萨加斯

Now we are at the stage where we can introduce our sagas in our project. If you are new to Redux-Saga then I suggest you tread this blog before proceeding.

现在我们处于可以在我们的项目中介绍我们的萨加斯酒的阶段。 如果您是Redux-Saga的新手,我建议您在继续之前踩下此博客

If you already know about sagas then go ahead and create a src/sagas folder. Create the index.js file, and place the below code into this file.

如果您已经了解Sagas,请继续创建src/sagas 夹。 创建index.js 文件,然后将以下代码放入该文件。

In the above file, first I am importing fork from effects and watchUserAuthentication from watchers — which does not exist yet but we will make that file next. Then I simply export a generator function and fork the watchUserAuthentication.

在上面的文件中,首先我从effectswatchUserAuthentication导入fork 来自watchers -尚不存在,但我们接下来将制作该文件。 然后,我仅导出一个生成器函数并派生watchUserAuthentication

Now go ahead and create a watcher.js file in the same folder as above, and place the below code into this file.

现在,继续在与上面相同的文件夹中创建一个watcher.js文件,并将以下代码放入该文件中。

Again, I import takeLatest effect from redux-saga, then registerSaga from authenticationSaga.js, which we will create next. Next, import actions/index.js as types.

再次,我导入takeLatest 来自redux-saga效果,然后来自authenticationSaga.js registerSaga ,我们接下来将创建它。 接下来,将actions/index.js导入为类型。

I am exporting a generator function which basically watches for the REGISTER_USER action and makes a call to registerSaga.

我正在导出一个生成器函数,该函数基本上监视REGISTER_USER动作并调用registerSaga

Now let’s create authenticatioSaga.js saga in same folder as above, and place the below code into this file.

现在,让我们在与上述相同的文件夹中创建authenticatioSaga.js传奇,并将以下代码放入该文件中。

In this saga I am importing a couple more effects — put and call from redux-saga. Then registerUserService is imported from service/authenticationService.js. I am importing all actions as types from actions/index.js. Then I am exporting the generator function registerSaga.

在这个传奇,我进口一对夫妇更多的效果- putcallredux-saga 。 然后从service/authenticationService.js导入registerUserService 。 我正在从actions/index.js导入所有动作作为类型。 然后我要导出生成器函数registerSaga

This function is responsible for calling registerUserService, which makes an ajax call to our server to register new user — which I will write after this step. It receives a response from registerUserService and puts the REGISTER_USER_SUCCESS action. If there is an error then it puts the REGISTER_USER_ERROR action.

这个函数负责调用registerUserService ,它对我们的服务器进行ajax调用以注册新用户-我将在此步骤之后编写该用户。 它收到registerUserService的响应,并执行REGISTER_USER_SUCCESS操作。 如果有错误,则将执行REGISTER_USER_ERROR操作。

Import the sagas

进口萨加斯

Now that we have our sagas it is time to import them in our store. Open store/configureStore.js and update its contents with the below contents.

现在我们有了萨加斯,是时候将它们导入我们的商店了。 打开store/configureStore.js并使用以下内容更新其内容。

Here I am importing createSagaMiddleware, rootReducer, and rootSaga. Then, inside the configureStore function, I am creating a new sagaMiddleware and passing it to createStore using the applyMiddleware function. Finally, I am running the rootSaga.

在这里,我要导入createSagaMiddlewarerootReducerrootSaga 。 然后,在configureStore函数内部,我将创建一个新的sagaMiddleware并将其使用applyMiddleware函数传递给createStore 。 最后,我正在运行rootSaga

Now it’s time to create the src/services folder and create a new first service. Name it authenticationService.js and place the below code into this service.

现在是时候创建src/services文件夹并创建一个新的第一个服务了。 将其命名为authenticationService.js 并将以下代码放入该服务。

This file does a basic ajax request using fetch API with some parameters and header. It is a pretty self-explanatory service.

该文件使用带有某些参数和标头的访存API进行基本的Ajax请求。 这是一项非常不言自明的服务。

Reducer

减速器

Now that we are making a request to the server, it is time to receive that response in our component. To do this we need a reducer. Go ahead and create a reducers/registerReducer.js file and place the below code into it.

现在我们正在向服务器发出请求,是时候在组件中接收该响应了。 为此,我们需要一个减速器 。 继续创建一个reducers/registerReducer.js 文件,并将以下代码放入其中。

It is a simple reducer function that gets state and returns new state. It checks for REGISTER_USER_SUCCESS and REGISTER_USER_ERROR actions, and returns the new state to the component.

这是一个简单的reducer函数,它获取状态并返回新状态。 它检查REGISTER_USER_SUCCESSREGISTER_USER_ERROR操作,并将新状态返回到组件。

Now go ahead and open the src/reducers/index.js file and update it with the following contents.

现在继续打开src/reducers/index.js 文件并使用以下内容进行更新。

In this rootReducer I will be importing all reducers and then combining them before exporting. That is exactly what I am doing with register.

在这个rootReducer我将导入所有的reducer,然后在导出之前将它们合并。 这正是我使用register所做的。

Running the updated code

运行更新的代码

Now we are done with the registration process. It is time to refresh your browser, go to the register route, and enter some data. If you enter an existing email then you should see the below result.

现在我们完成了注册过程。 现在该刷新浏览器,转到注册路线,然后输入一些数据。 如果输入现有电子邮件,则应看到以下结果。

If you enter a new email then you should be redirected to loginPage, which we are going to implement next.

如果您输入新的电子邮件,则应将您重定向到loginPage ,接下来我们将实现它。

登录 (Login)

It is time for us to login the user after they are registered. Go ahead and open components/loginPage.js file and update it with the following contents.

现在是时候让我们在用户注册后登录用户了。 继续并打开components/loginPage.js 文件并使用以下内容进行更新。

This component is pretty much the same as registerPage. The only difference is that it dispatches loginUserAction which we are going to write next. Another difference is that, if the response from the server is successful, I will receive a JWT token. I am storing that token in localStorage. You can use a different method but for this example I am using this approach.

该组件与registerPage几乎相同。 唯一的区别是它调度loginUserAction 接下来我们要写。 另一个区别是,如果服务器响应成功,我将收到JWT token 。 我将令牌存储在localStorage 。 您可以使用其他方法,但是对于本示例,我正在使用此方法。

Go ahead and open actions/authenticationActions.js and update it with the following contents.

继续并打开actions/authenticationActions.js 并使用以下内容进行更新。

Here I am exporting the new loginUserAction function with LOGIN_USER action type and user payload.

在这里,我将导出具有LOGIN_USER操作类型和user payload的新loginUserAction函数

Before moving forward, go ahead and open the actions/index.js file and update its contents with the following.

在继续之前,请继续并打开actions/index.js文件,并使用以下内容更新其内容。

Now go ahead and open the sagas/watchers.js file and update its contents with the following.

现在继续打开sagas/watchers.js 文件并使用以下内容更新其内容。

Here I am simply importing loginSaga and calling it when it receives the LOGIN_USER action.

在这里,我只是导入loginSaga并在收到LOGIN_USER时调用它 行动。

We do not have loginSaga yet. For that reason go ahead and open the sagas/authenticationSaga.js saga and update its contents with the following.

我们还没有loginSaga 。 因此,继续打开sagas/authenticationSaga.js 并使用以下内容更新其内容。

Here I am importing an additional service — loginUserService, which I will be implementing next — and then exporting the new generator function named loginSaga, which does pretty much the same thing as registerSaga.

在这里,我要导入一个附加服务(我将在下一步中实现该操作) loginUserService 然后导出名为loginSaga的新生成器函数,该函数与registerSaga几乎相同。

Now open the services/authenticationService.js service and update its contents with the following.

现在打开services/authenticationService.js 服务并使用以下内容更新其内容。

Here I am adding loginUserService which does pretty much the same as registerUserService i.e. sending an ajax request to login the user.

在这里,我添加了loginUserService,它与registerUserService几乎一样,即发送ajax请求以登录用户。

Now that we’ve successfully sent a request to the server it is time to receive a response from our server to our login component. For that create a new reducers/loginReducer.js reducer and place the below code into it.

现在我们已经成功向服务器发送了请求,是时候接收服务器对登录组件的响应了。 为此,创建一个新的reducers / loginReducer.js reducer并将以下代码放入其中。

It does pretty much the same thing as registerReducerlistening to LOGIN_USER_SUCCESS and LOGIN_USER_ERROR actions, and returning the new state.

它几乎同样的事情registerReducer -LOGIN_USER_SUCCESSLOGIN_USER_ERROR行动,并返回新的状态。

Now open the reducers/index.js file and update its contents with the code below.

现在打开reducers/index.js 文件并使用以下代码更新其内容。

Here I am importing loginReducer and combining it with register before returning it as rootReducer.

在这里,我要导入loginReducer并将其与register结合,然后将其返回为rootReducer

After this, refresh your browser and enter an email that is not registered yet. After pressing the login button you should see the below result.

之后,刷新浏览器并输入尚未注册的电子邮件。 按下登录按钮后,您应该看到以下结果。

If you enter a registered email then the request should be successful, but you should not see anything yet, as I haven’t implemented the dashboardPage component. This will only be accessed after successful authentication. Having said that, let’s implement it.

如果您输入注册的电子邮件,则请求应该会成功,但是您应该看不到任何内容,因为我尚未实现dashboardPage 零件。 仅在成功认证后才能访问。 话虽如此,让我们实现它。

资讯主页 (Dashboard page)

Now create the components/dashboardPage.js component and place the below code into this component.

现在创建components/dashboardPage.js 组件,然后将以下代码放入该组件。

This is a very simple component — all it does is return the Dashboard text.

这是一个非常简单的组件-它所做的只是返回Dashboard文本。

Now open the routes/index.js route and update its contents with the following.

现在打开routes/index.js 路由并使用以下内容更新其内容。

Here I am doing some new stuff. First I am importing a dashboardPage and adding it to route. When the dashboard route is accessed the requireAuth function will be triggered. This function checks if the user is loggedIn or not. To check that, I am looking for token in localStorage, which I stored in the loginPage component on successful login. If it does exist, then dashboardPage is rendered to the user.

在这里,我正在做一些新的东西。 首先,我要导入一个dashboardPage并将其添加到route 。 当dashboard 路由被访问时, requireAuth函数将被触发。 此功能检查用户是否已loggedIn 。 要检查一下,我正在寻找token localStorage 成功登录loginPage其存储在loginPage组件中。 如果确实存在,则将dashboardPage呈现给用户。

Now when you refresh page in your browser, enter a registered email, and press enter, you should see the below results.

现在,当您在浏览器中刷新页面时,输入注册的电子邮件,然后按Enter,您应该看到以下结果。

So there it is, this is a complete login system using React, Redux and Redux-Saga. If you would like to see the whole project then clone this repository.

就是这样,这是一个使用React,Redux和Redux-Saga的完整登录系统。 如果您想查看整个项目,请克隆此存储库

I hope you enjoyed this post.

希望您喜欢这篇文章。

翻译自: https://www.freecodecamp.org/news/login-using-react-redux-redux-saga-86b26c8180e/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值