Godkendelse med React Form Wizard og Nodejs - Del 1

Introduktion
I Gør enhver formular til en stepper-formularguide med UI, Hooks, Context, React-Hook-Form og Yup blev det vist, hvordan du kan forbedre brugeroplevelsen ved at opdele udvidede formularer i guider med React, Material-UI og React-Hook- Formularer. Denne tutorial har til formål at kode et log ind og tilmelde stepper drevet af en Nodejs back-end og bruger den samme arkitektur fra den forrige del med undtagelse af Redux, som vil blive brugt til at administrere tilstand på applikationsniveau.

Så hvad vil blive kodet?

(1) Node.js back-end til registrering og validering af brugere

(2) Redux-lager til kommunikation med back-end

(3) Tilmeldingsformularguiden med Context Store

(4) Log på formularguiden med Context Store

Forudsætninger
For at kunne arbejde med begreberne præsenteret i denne tutorial bør du have en grundlæggende forståelse af ES6, React hooks, funktionelle komponenter, Context, Redux og NodeJS. T

I denne første del sætter vi back-end-serveren og Redux Store op. I den næste del vil vi kode formularguidens steppere. Koden til dette projekt kan findes på Github.

Lad os starte med at installere en ny React-app.

npx create-react-app <your-app-name>
cd <your-app-name>
npm install

(1) NodeJS back-end
Lad os først kode serveren. I din projektmappe opret en mappe kaldet server . Brug nu din yndlingspakkemanager til at starte din lokale repo:

npm init

Installer nu følgende pakker:

npm i --save cors express lowdb morgan nanoid

Installer nodemon.js globalt, et værktøj, der hjælper med at udvikle node.js-baserede applikationer ved automatisk at genstarte nodeapplikationen, når filændringer i mappen registreres. Installer det globalt med

npm i -g nodemon

Du har installeret lowdb, en lille lokal JSON-database til små NodeJS-projekter. Denne pakke gemmer data som et objekt og understøtter to operationer:læse og skrive. Serverappen vil bruge Express til at registrere, læse og opdatere brugerobjekter/indgange og nanoid til at oprette brugertokens.

Lad os nu oprette nodeindeksfilen, der vil blive serveret med nodemon.js.

index.js

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: '[email protected]' ,
    password: '12345678',
    firstName: 'Admin',
    lastName: 'Adminstrator',
    token: nanoid(30) 
  },
  {
    id: 2,
    role: 'user',
    email: '[email protected]',
    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}`));

Denne fil initialiserer databasen med to foruddefinerede brugerkonti og fortæller Express at bruge ruter fra users.js fil. Så lad os tilføje denne fil:

users.js

Din server er nu klar til at køre på port 4000.
Så lad os starte det med

npm start

Du kan teste registreringen for enhver bruger fra din browse med denne GET-rute:

http://locahost:4000/register/[email protected]/mypassword

(2) Redux-butik til kommunikation med back-end
Lad os nu flytte en dir op, til roden af ​​mappen og tilføje følgende pakker til React-appen:

npm i --save @hookform/resolvers @mui/icons-material 
@mui/material @reduxjs/toolkit react-hook-form 
react-hot-toast react-redux yup

Hvorfor ville du implementere Redux, hvis React Context kan klare opgaven? Det er en meningssag. Redux har bedre kodeorganisering, gode værktøjer til fejlretning, designet til dynamiske data og kan udvides, som det kan læses i denne artikel. En anden stor fordel er brugen af ​​thunks eller middleware, der kan importeres til andre udsnit eller dele af din butik. Men når du koder et lille projekt, er Redux sandsynligvis en form for overhead.

Lad os nu kode Redux-butikken:

  1. UserSlice
  2. Butik
  3. Indpak appen med Redux

Opsætning af UserSlice

UserSlice indeholder to funktioner, der kan bruges med Redux's afsendelse og getstate metoder, som vil blive kaldt i højordenskomponenten i vores formularguide. Status for disse handlinger administreres i extraReducers afsnit. Faktisk ville det være bedre at eksportere disse handlinger til en separat fil, så de kan kaldes og bruges i andre udsnit. Inde i src/ mappe i din mappe opret en ny mappe med navnet Butik og kode 'UserSlice.js'.

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

Lad os først oprette en indpakningsfunktion til hentningsanmodninger og importere relevante komponenter.

/* 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;      
}

Nu har vi brug for to middleware-funktioner, én til registrering af nye brugere og én til at logge ind. Disse funktioner er oprettet med Redux createAsyncThunk, så vores app har adgang til livscykluserne for async-anmodninger, der er afvist, afventende og opfyldt, som kan bruges til at administrere applikationens tilstand .

Login funktionen:

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
      });
    }
  }
)

Og registreringsfunktionen:

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
      });
    }
  }
);

Lad os nu kode skivedelen:

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;

The Redux Store
Opsæt nu butikken, der samler den tilstand, handlinger og reducering, der udgør appen, så tilstanden kan hentes, opdateres og håndtere tilbagekald. Opret filen src/Store/index.js:

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

const rootReducer = combineReducers({
  user: UserSlice
});
export const store = configureStore({
  reducer: rootReducer,
});

Indpak appen med Redux
Til sidst 'indpak appen' med Redux ved at redigere din src/index.js-fil:

Den globale butik er nu klar til at blive importeret til vores form stepper-moduler.

Denne vejledning fortsætter i Autentificering med React From Wizard og Nodejs - Del 2, som forklarer, hvordan man koder godkendelsesformularguiderne. Koden til dette projekt kan findes på Github.