Authentication with React Form Wizard and Nodejs - Part 1
Published: 2022-03-12 15:03:48

**Introduction**
In [Turn any Form into a stepper form wizard with UI, Hooks, Context, React-Hook-Form and Yup](https://dev.to/paulvermeer2021/turn-any-form-into-a-stepper-form-wizard-with-ui-hooks-context-react-hook-form-and-yup-56j6) it was shown how you can improve user experience by breaking up extended forms into wizards with React, Material-UI and React-Hook-Forms. This tutorial aims to code a sign in and sign up stepper powered by a Nodejs back-end and uses the same architecture from the previous part with the exception of Redux which will be used to manage state at application level. 


So what will be coded?

(1) Node.js back-end for registering and validating users

(2) Redux store for communicating with back-end

(3) Sign up form wizard with Context Store

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i6vb1dgtuav5x3alf8ca.png)

(4) Sign in form wizard with Context Store

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ix19t966xifkbjzo0gj3.png)

**Prerequisites**
In order to work with the concepts presented in this tutorial you should have a basic understanding of ES6, React hooks, functional components, Context, Redux and NodeJS.  T

In this first part we'll set up the back-end server and the Redux Store. In the next part we'll code the form wizard steppers. The code for this project can be found on [Github](https://github.com/full-stack-concepts/react-form-wizards).

Let's start with installing a new React [app](https://reactjs.org/docs/create-a-new-react-app.html).
```javascript
npx create-react-app <your-app-name>
cd <your-app-name>
npm install
```

**(1) NodeJS back-end**
Let's code the server first. In your project directory <your-app-name> create a folder called <em>server</em>. Now use your favorite package manager to init your local repo:

```javascript
npm init
```
Now install the following packages:

```javascript
npm i --save cors express lowdb morgan nanoid
```
Install [nodemon.js](https://github.com/remy/nodemon) globally, a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected. Install it globally with 

```javascript
npm i -g nodemon
```
You have installed [lowdb](https://www.npmjs.com/package/lowdb), a tiny local JSON database for small NodeJS projects. This package stores data as an object and supports two operations: read and write. The server app will use [express](https://expressjs.com/) to register, read and update user objects/entries and [nanoid](https://github.com/ai/nanoid) to create user tokens. 

Let's now create the node index file that will be served with [nodemon.js](https://github.com/remy/nodemon).

<i>index.js</i>
```javascript
import express from "express";
import cors from 'cors';
import morgan from "morgan";
import { Low, JSONFile } from 'lowdb';
import userRouter from './users.js';
import { nanoid } from 'nanoid';

const adapter = new JSONFile("db.json");
const db = new Low(adapter);
db.data = ({ users: [
  { 
	id: 1,
	role: 'admin',
	email: 'admin@example.com' ,
	password: '12345678',
	firstName: 'Admin',
	lastName: 'Adminstrator',
	token: nanoid(30) 
  },
  {
	id: 2,
	role: 'user',
	email: 'johndoe@example.com',
	password: '12345678',
	firstName: 'John',
	lastName: 'Doe',
	token: nanoid(30)
  }
]});
await db.write(db.data);

const PORT = process.env.PORT || 4000
const app = express();
app.db = db;
app.use(cors({origin: '*'}));
app.use(express.json());
app.use(morgan("dev"));
app.use("/users", userRouter);
const localRouter = express.Router();
localRouter.get("/", (req, res) => {		
  res.send('Only  /users/* routes are supported ');
});
app.use(localRouter);
app.listen(PORT, () => console.log(`Listening on Port ${PORT}`));
```
This file initializes the database with two predefined user accounts and tells express to use routes from the <em>users.js</em> file. So let's add this file:

<em>users.js</em>
{% gist 
https://gist.github.com/full-stack-concepts/73524402e223790b8c0b2afe50d11f9f
%}

Your server is now ready to run on port 4000. 
So let's start it with 
```javascript
npm start
```
You can test the registration for any user from your browse with this GET route:
```
http://locahost:4000/register/user@example.com/mypassword
```

**(2) Redux store for communicating with back-end**
Let's now move one dir up, to the root of <your-app-name> directory and add the following packages to the React app:

```javascript
npm i --save @hookform/resolvers @mui/icons-material 
@mui/material @reduxjs/toolkit react-hook-form 
react-hot-toast react-redux yup
```
Why would you implement [Redux](https://redux.js.org/) if React Context can do the job? That's is a matter of opinion. Redux has better code organization, great tools for debugging, designed for dynamic data and extendible as can be read in this [article](https://dev.to/ruppysuppy/redux-vs-context-api-when-to-use-them-4k3p). Another great advantage is the usage of thunks or middleware that can be imported into other slices or parts of your store. But when you code a small project Redux is probably a form of overhead. 

Let's now code the Redux store:
<ol>
<li>UserSlice</li>
<li>Store</li>
<li>Wrap the App with Redux</li>
</ol>

<em>Setting up the UserSlice</em>

The UserSlice contains two functions that can be used with Redux's <em>dispatch</em> and <em>getstate</em> methods which will be called in the high order component of our form wizard. The state of these actions is managed in the <em>extraReducers</em> section. Actually it would be better to export these actions into a separate file so they can be called and used in other slices. Inside the <em>src/</em> folder of your <your-app-name> folder create a new folder named <em>Store</em> and code 'UserSlice.js'.

``` 
 <your-app-name>/src/Store/UserSlice.js
```

Let's first crate a wrapper function for fetch requests and import relevant components. 

```javascript
/* eslint-disabled  */
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

const request = async (method, url, data) => {
  let response = await fetch(
    url,                
    {
      method,
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    }
  );
  return response;      
}
```

Now we need two middleware functions, one for registering new users and one for signing in. These functions are created with [Redux createAsyncThunk](https://redux-toolkit.js.org/api/createAsyncThunk) so our app has access to the async request lifecycles rejected, pending and fulfilled which canbe used to manage the state of the application.

The login function:
```javascript
export const loginUser = createAsyncThunk(
  'user/login',
  async ({ email, password }, thunkAPI) => {
    try {
      const url = 'http://localhost:4000/users/login';    
      const response = await request('POST', url, { email, password });           
      const data = await response.json();        
      if (response.status === 200) {                
        return { 
          ...data,                 
          status: true
        };
      } else {
        return thunkAPI.rejectWithValue(data);
      }
    } catch (e) {            
      return thunkAPI.rejectWithValue({
        status:false,
        data: e.response.data
      });
    }
  }
)
```
And the registration function:
```javascript
export const signupUser = createAsyncThunk(
  'user/signup',
  async ({ email, password }, thunkAPI) => {    
    try {
      const url = 'http://localhost:4000/users/register';            
      const response = await request('POST', url, { email, password });           
      let data = await response.json();                
      if (response.status === 200 || response.status === 201) {                
        return { 
          ...data, 
          email: email,
          status: data.status,
          message: (data.message) ? data.message: null
        }
      } else {                  
        return thunkAPI.rejectWithValue(data);
      }
    } catch (e) {            
      return thunkAPI.rejectWithValue({
        status: false,
        data: e.response.data
      });
    }
  }
);
``` 
Let's now code the slice portion: 

```javascript
const initFetchState = {
  fetching: false,
  success: false,
  error: false,
  message: null
}

const initMemberState = {
  token: null,  
  email: null        
}

const initialState = {
  loggedIn:false,
  status: initFetchState,
  member: initMemberState
};

const userSlice = createSlice({
  name: 'user',
  initialState: initialState,
  reducers: {    	
    clearState: state => { state = initialState; },    
    clearFetchStatus: state => {
      state.status = initFetchState;
    },
    deleteUserToken: state => {
      state.member = { ...state.member, token: null};
    },
    setuserToken: (state, action) => { 
      state.member = { ...state.member, token: action.payload };
    },
    logout: (state, action) => { 
      state = { 
        loggedn: false,
        status: initFetchState,
        member: initMemberState
      };
    },
  },
  extraReducers: {
    [signupUser.fulfilled]: (state, { payload }) => {          
      state.status.fetching = false;
      state.status.success = true;          
      state.member.email = payload.email;      	
      return state;
    },
    [signupUser.pending]: (state) => {
      state.status.fetching = true;
      return state;
    },
    [signupUser.rejected]: (state, { payload }) => {                     
      state.status.fetching= false;
      state.status.error = true;
      state.status.message = (payload) ? payload.message : 'Connection Error';            
      return state;
    },
    [loginUser.fulfilled]: (state, { payload }) => {                                        
      state.loggedIn = true;
      state.member.token = payload.token;
      state.member.email = payload.user.email;
      state.member.id = payload.user.id;        
      state.status.fetching = false;
      state.status.success = true;
      return state;
    },
    [loginUser.rejected]: (state, { payload }) => {	                       
      state.status.fetching= false;
      state.status.error = true;	           
      state.status.message = (payload) ? payload.message : 'Connection Error';           
      return state;
    },
    [loginUser.pending]: (state) => {       
      state.status.fetching = true;
      return state;
    },  	
  }
});

export const {
  clearState,   
  setuserToken,
  clearFetchStatus
} = userSlice.actions;

export default userSlice.reducer;

```
<em>The Redux Store</em>
Now set up the store that brings together the state, actions, and reducers that make up the app so state can be retrieved, updated and handle callbacks. Create the src/Store/index.js file:

```javascript
import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
import UserSlice from './UserSlice';

const rootReducer = combineReducers({
  user: UserSlice
});
export const store = configureStore({
  reducer: rootReducer,
});
```
<em>Wrap the App with Redux</em>
Finally 'wrap the app' with Redux by editing your src/index.js file:
{% gist 
https://gist.github.com/full-stack-concepts/a525e1eb6218c23af9f7ebef21f8856d
%}

The global store is now ready to be imported into our form stepper modules.

This tutorial continues in [Authentication with React From Wizard and Nodejs - Part 2](https://dev.to/paulvermeer2021/authentication-with-react-from-wizard-and-nodejs-part-2-1n2d) which explains how to code the authication form wizards. The code for this project can be found on [Github](https://github.com/full-stack-concepts/react-form-wizards).