DEV Community

Cover image for Authentication with React Form Wizard and Nodejs - Part 2
full-stack-concepts
full-stack-concepts

Posted on • Updated on

Authentication with React Form Wizard and Nodejs - Part 2

This tutorial is a continuation of 'Authentication with React From Wizard and Nodejs - Part 1. In the previous part the backend and Redux Store was coded. It's now time to build the form wizards with material-UI, React Hooks and Yup.

(3) Sign Up Form Wizard with Context Store

Image description

The first thing we need to do is to define the main form element field, a Material-UI text field. Create a folder named elements inside your /src directory and add TextField.js.

You can now use this element to code the inputs or fields that will be used in the hooks form. Create an inputs folder in /src directory and add these fine files:

Email.js

import TextField from '../elements/TextField';

export const Email = ({label, control, register, onChangeHandler, onBlurHandler}) => <TextField
    fieldName={`email`}
    fieldType={`email`}
    label={`Email`}
    control={control}
    register={register}
    required={true}
    onChangeHandler={onChangeHandler}
    onBlurHandler={onBlurHandler}
/>
Enter fullscreen mode Exit fullscreen mode

Password.js

import TextField from '../elements/TextField';

export const Password = ({label, control, register, onChangeHandler, onBlurHandler}) => <TextField
    fieldName={`password`}
    fieldType={`password`}
    label={`Password`}
    control={control}
    register={register}
    required={true}
    onChangeHandler={onChangeHandler}
    onBlurHandler={onBlurHandler}
/>
Enter fullscreen mode Exit fullscreen mode

Signin Components

Now create a components folder in /src dir and then a new subfolder named SigninStepper.

Our main component is Stepper.js which imports it's children dynamically, depending on the form step. Each form step should be validated as soon as all fields are touched. If the step is valid the user should be allowed to progress to the next step. All components share state through React Context.

Image description

Let's start with coding a local Context Store, FormContext.js

import {createContext, useState} from 'react';
export const FormContext = createContext();
export const FormContextProvider = ({children}) => {    
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [step1, setStep1Finished ] = useState(false);   
  const [finished, setFinished] = useState(false);
  const formContextValues = {               
    email, setEmail,  
    password, setPassword,
    step1, setStep1Finished,        
    finished, setFinished
  };    
  return (<div>
    <FormContext.Provider value={formContextValues}>
      {children}
    </FormContext.Provider>
   </div>);
}
Enter fullscreen mode Exit fullscreen mode

Then wrap your provider like so in components/SigninStepper/index.js:

import React from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { green, blue } from '@mui/material/colors';
import { FormContextProvider } from './FormContext';
import Stepper from './Stepper';
const theme = createTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: blue[500],
    },
    secondary: {
      main: green[500],
    },
  },
});

/**
 * Form Context Store
 */
function SigninStepper() {  
  return (
    <ThemeProvider theme={theme}>
      <FormContextProvider>              
        <Stepper />
      </FormContextProvider>
     </ThemeProvider>
  );
}

export default SigninStepper;
Enter fullscreen mode Exit fullscreen mode

The Stepper Component
The 'high order component'is a @Mui vertical stepper and extended with:
(1) The FormContext store.

    const {
        email, password, step1, finished       
    } = useContext(FormContext);
Enter fullscreen mode Exit fullscreen mode

(2) Load dynamic content with useEffect hook.

   useEffect(() => {       
        let Component;
        const load = async () => {
            const StepView = `Step${activeStep+1}`;
            if(!components[StepView]) {             
                const { default:View } = await import(`./Steps/${StepView}`)
                Component = <View 
                    FormContext={FormContext} 
                />;             
                setComponent({...components, [StepView]: Component })
                setView(Component);
            } else {               
                setView(components[StepView]);
            }
        }
        load();       
    }, [activeStep]); 
Enter fullscreen mode Exit fullscreen mode

(3) Monitor progress with hook useEffect

useEffect(() => {    
  setSolutionProvided(false);
  if (activeStep === 0 && step1) {
    setSolutionProvided(true);
  }       
  if (activeStep === steps.length - 1 && password && finished) {
    setSolutionProvided(true);
  }       
}, [activeStep, email, password, step1, finished]);
Enter fullscreen mode Exit fullscreen mode

(4) Monitor result of our Redux login action with another useEffect hook.

useEffect(() => {            
  if (status.success) {                             
    navigate('/dashboard');
  }       
  if (status.error) {                     
    setActiveStep(steps.length-1);
    toast.error(status.message);                          
  }
  dispatch(clearFetchStatus());       
}, [status, dispatch, navigate]);
Enter fullscreen mode Exit fullscreen mode

The result may look like:

Let's now create the /src/components/SigninStepper/Stepsdirectory where the different form steps will be stored.

Step1.js

Whenever the user add a value to the email field the onChangeHandler function is triggered and (1) injects the event value into the form state of React Hook Form, (2) triggers validation and (3) saves the value in the Context Store.

As soon as Yup through React Hook Form signals that the form is valid - the isValid param of formState - the high order component (Stepper.js) is informed though an useEffect hook that the step is finished and the user may proceed to the next one.

Step2.js

This step uses the same logic. Whenever a user browses between validated steps the useEffect hook is used to load any value saved in the Context Store.

(4) Sign Up form wizard with Context Store

Image description

To create this form wizard you only have to repeat the steps used to create the Sign in Form Wizard as it uses the same building blocks:
(1) Create a folder named SigninStepper
(2) Code the Context Store
(3) Wrap it around Stepper.js in index.js
(4) Code Stepper.js
(5) Code the steps with inputs Email and Pasword

The app is now ready to be tested. Open two terminals and start the server and client with npm run start.

More examples can be found in the repo.

Top comments (0)