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
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}
/>
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}
/>
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.
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>);
}
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;
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);
(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]);
(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]);
(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]);
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
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)