banner



How To Animate Sign In Process React

In this article, we will build an amazing Multi Stride Registration form with smooth animated transitions using the MERN stack (MongoDB, Limited, React, and Node.js).

By building this App, yous will acquire a lot of concepts in React and Node.js including:

  • How to manage data for multiple forms with validation for each field
  • How to retain values of forms data across routes
  • How to update progress indications for each registration footstep
  • How to load land-specific state and city from the API
  • How to create polish sliding animations using a very popular framer-motion library
  • How to create Balance APIs using Limited.js
  • How to implement login and registration functionality with MongoDB
  • How to shop and validate passwords stored in encrypted form in MongoDB

And much more than.

We volition be using React Hooks syntax for building this application in React. So if you're new to React Hooks, cheque out my Introduction to React Hooks article to learn the basics of Hooks.

Nosotros will also be using a MongoDB database to shop the registered user data, then make certain you install MongoDB locally by following instructions from this article.

Alright, allow's go started.

Initial Project Setup

Create a new project using create-react-app:

          npx create-react-app multi-step-class-using-mern                  

One time you've created the project, delete all files from the src folder and create an alphabetize.js file and a styles.scss file within the src folder. Also create components, router,  and utils folders inside the src folder.

Install the necessary dependencies like this:

          yarn add together axios@0.21.ane bootstrap@4.6.0 react-bootstrap@1.5.0 land-land-city@ii.0.0 framer-move@three.seven.0 node-sass@4.xiv.1 react-claw-class@6.15.iv react-router-dom@v.2.0 sweetalert2@10.15.five                  

Open up your styles.scss file and add the contents from here inside information technology.

We'll use SCSS syntax to write CSS. So if y'all're new to SCSS, check out my commodity here for an introduction to it.

How to Create the Initial Pages

Create a new file Header.js inside the components binder with the post-obit content:

          import React from 'react';  const Header = () => (   <div>     <h1>Multi Step Registration</h1>   </div> );  export default Header;                  

Create a new file FirstStep.js inside the components folder with the post-obit content:

          import React from 'react';  const FirstStep = () => {   return (     <div>       Kickoff Footstep Form     </div>   ) };  export default FirstStep;                  

Create a new file AppRouter.js within the router folder with the following content:

          import React from 'react'; import { BrowserRouter, Road, Switch } from 'react-router-dom'; import FirstStep from '../components/FirstStep'; import Header from '../components/Header';  const AppRouter = () => (   <BrowserRouter>     <div className="container">       <Header />       <Switch>         <Route component={FirstStep} path="/" verbal={truthful} />       </Switch>     </div>   </BrowserRouter> );  consign default AppRouter;                  

In this file, initially, we have added a single road for the first step.

If you lot're new to React Router, check out my gratuitous Introduction to React Router course.

Now, open the src/index.js file and add together the following content within information technology:

          import React from 'react'; import ReactDOM from 'react-dom'; import AppRouter from './router/AppRouter'; import 'bootstrap/dist/css/bootstrap.min.css'; import './styles.scss';  ReactDOM.render(<AppRouter />, certificate.getElementById('root'));                  

Start the application past running the yarn start control and you will see the following screen:

multi_initial_screen

Create a new file called Progress.js within the components binder with the following content:

          import React from 'react';  const Progress = () => {   return (     <React.Fragment>       <div className="steps">         <div className="step">           <div>1</div>           <div>Step i</div>         </div>         <div className="step">           <div>ii</div>           <div>Pace 2</div>         </div>         <div className="stride">           <div>3</div>           <div>Step 3</div>         </div>       </div>     </React.Fragment>   ); };  export default Progress;                  

and employ it inside the Header.js file every bit shown below:

          import React from 'react'; import Progress from './Progress';  const Header = () => (   <div>     <h1>Multi Step Registration</h1>     <Progress />   </div> );  export default Header;                  

At present, if you check the awarding, you lot will see the following screen:

with_progress

How to Create the First Step Grade

Open the components/FirstStep.js file and replace what's in at that place with the following contents:

          import React from 'react'; import { useForm } from 'react-claw-class'; import { Course, Push } from 'react-bootstrap';  const FirstStep = (props) => {   const { register, handleSubmit, errors } = useForm();    const onSubmit = (data) => {     panel.log(data);   };    return (     <Form className="input-course" onSubmit={handleSubmit(onSubmit)}>       <div className="col-doctor-6 kickoff-md-3">         <Form.Group controlId="first_name">           <Class.Characterization>First Name</Class.Characterization>           <Form.Command             type="text"             name="first_name"             placeholder="Enter your commencement name"             autoComplete="off"             ref={register({               required: 'First proper noun is required.',               blueprint: {                 value: /^[a-zA-Z]+$/,                 message: 'First name should incorporate simply characters.'               }             })}             className={`${errors.first_name ? 'input-mistake' : ''}`}           />           {errors.first_name && (             <p className="errorMsg">{errors.first_name.message}</p>           )}         </Course.Grouping>          <Form.Group controlId="last_name">           <Form.Label>Terminal Proper noun</Course.Label>           <Form.Control             blazon="text"             proper noun="last_name"             placeholder="Enter your terminal name"             autoComplete="off"             ref={register({               required: 'Concluding name is required.',               pattern: {                 value: /^[a-zA-Z]+$/,                 message: 'Last proper noun should contain only characters.'               }             })}             className={`${errors.last_name ? 'input-mistake' : ''}`}           />           {errors.last_name && (             <p className="errorMsg">{errors.last_name.message}</p>           )}         </Course.Group>          <Button variant="chief" blazon="submit">           Adjacent         </Button>       </div>     </Form>   ); };  export default FirstStep;                  

Here, nosotros're using a very popular react-hook-form library to easily manage forms with validations.

React-hook-form makes it really piece of cake to work with elementary equally well as circuitous forms, as we don't need to manage the land of each input field and its onChange handler ourselves. This makes the code cleaner and easier to empathize.

Check out my commodity hither to learn well-nigh react-hook-grade in detail.

Equally you can run across in the above code, to utilise the react-hook-form library we need to commencement import and use the useForm hook.

                      const { annals, handleSubmit, errors } = useForm();                  

Hither,

  • register is a function that we'll employ every bit a ref provided by the useForm hook. We can assign it to each input field and then that the react-hook-form can track the changes for the input field value
  • handleSubmit is the part we can call when the grade is submitted
  • errors volition contain the validation errors, if whatever

In the in a higher place code, we take given a ref to each input field that we got from the useForm hook like this:

          ref={register({   required: 'First name is required.',   pattern: {     value: /^[a-zA-Z]+$/,     message: 'Offset name should contain only characters.'   } })}                  

Also, nosotros added the onSubmit function which is passed to the handleSubmit function.

          <Form className="input-form" onSubmit={handleSubmit(onSubmit)}>                  

Note that for each input field, we have given a unique name which is mandatory so react-hook-course can rail the changing data.

When we submit the form, the handleSubmit function will handle the form submission. It will transport the user entered data to the onSubmit role which we're logging to the console.

          const onSubmit = (information) => {    console.log(information); };                  

If there are any errors, we'll brandish them like this:

          {errors.first_name && (   <p className="errorMsg">{errors.first_name.message}</p> )}                  

The errors object will be automatically populated with the holding name denoted by the name given to each input field (if there are any errors). first_name in the above example is the name given to the outset input field.

Now, let'due south check the application's functionality:

first_step_form

As you can encounter, with very picayune code, nosotros've added a responsive validation functionality to the form.

How to Create the 2d Footstep Form

Now, create a new file SecondStep.js within the components binder with the post-obit content:

          import React from 'react'; import { useForm } from 'react-claw-course'; import { Form, Push } from 'react-bootstrap';  const SecondStep = (props) => {   const { annals, handleSubmit, errors } = useForm();    const onSubmit = (information) => {     panel.log(data);   };    render (     <Form className="input-form" onSubmit={handleSubmit(onSubmit)}>       <div className="col-md-vi start-dr.-3">         <Form.Group controlId="first_name">           <Form.Characterization>Email</Form.Label>           <Form.Control             type="email"             proper noun="user_email"             placeholder="Enter your email address"             autoComplete="off"             ref={register({               required: 'Email is required.',               pattern: {                 value: /^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/,                 message: 'E-mail is not valid.'               }             })}             className={`${errors.user_email ? 'input-error' : ''}`}           />           {errors.user_email && (             <p className="errorMsg">{errors.user_email.message}</p>           )}         </Form.Grouping>          <Grade.Grouping controlId="password">           <Form.Label>Password</Form.Label>           <Form.Control             type="password"             proper noun="user_password"             placeholder="Cull a countersign"             autoComplete="off"             ref={annals({               required: 'Password is required.',               minLength: {                 value: 6,                 bulletin: 'Countersign should accept at-least 6 characters.'               }             })}             className={`${errors.user_password ? 'input-mistake' : ''}`}           />           {errors.user_password && (             <p className="errorMsg">{errors.user_password.bulletin}</p>           )}         </Grade.Group>          <Push button variant="primary" type="submit">           Next         </Push button>       </div>     </Grade>   ); };  export default SecondStep;                  

Now, let's add another route in the AppRouter.js file for the SecondStep component.

          import React from 'react'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import FirstStep from '../components/FirstStep'; import Header from '../components/Header'; import SecondStep from '../components/SecondStep';  const AppRouter = () => (   <BrowserRouter>     <div className="container">       <Header />       <Switch>         <Route component={FirstStep} path="/" exact={true} />         <Route component={SecondStep} path="/second" />       </Switch>     </div>   </BrowserRouter> );  export default AppRouter;                  

As well, import the SecondStep component at the top of the file as shown to a higher place.

At present, nosotros've added a route for the 2d step, let'south check the application by accessing the URL  http://localhost:3000/second.

second_step_form

Every bit you can see, the functionality is working fine, simply we're directly accessing the /second road. Instead, let's add the code to programmatically redirect from step 1 to footstep 2.

When nosotros provide any component for the Route inside the BrowserRouter, React Router automatically passes 3 props to that component, which are:

  • history
  • location
  • match

Out of these, the history object contains a push button method that we tin utilise to redirect from one component to another.

So open the FirstStep.js file and supercede the onSubmit function with the post-obit code:

          const onSubmit = (data) => {   console.log(data);   props.history.push('/second'); };                  

Here, for the push method, nosotros've provided the route to which we demand to redirect.

redirection

As you can meet, when we click on the Side by side button in the first pace we're redirected to the 2d step.

Now, create a new file constants.js inside the utils folder with the following content:

          export const BASE_API_URL = 'http://localhost:3030';                  

Hither, we're specifying our backend API's URL then we don't need to specify information technology in every API call. Nosotros just demand to employ this abiding when we need to make an API call.

Now, let's add another route in our AppRouter.js file for the ThirdStep component.

          ... <Switch>   <Road component={FirstStep} path="/" exact={true} />   <Route component={SecondStep} path="/2d" />   <Road component={ThirdStep} path="/third" /> </Switch> ...                  

How to Become a List of All Countries from the API

Create a new file ThirdStep.js inside the components folder with the following content:

          import React, { useState, useEffect } from 'react'; import { Form, Button } from 'react-bootstrap'; import csc from 'country-state-metropolis'; import axios from 'axios'; import { BASE_API_URL } from '../utils/constants';  const ThirdStep = (props) => {   const [countries, setCountries] = useState([]);   const [states, setStates] = useState([]);   const [cities, setCities] = useState([]);   const [isLoading, setIsLoading] = useState(false);    const [selectedCountry, setSelectedCountry] = useState('');   const [selectedState, setSelectedState] = useState('');   const [selectedCity, setSelectedCity] = useState('');    useEffect(() => {    const getCountries = async () => {      attempt {        const consequence = await csc.getAllCountries();        console.log(issue);      } catch (fault) {}     };      getCountries();   }, []);    const handleSubmit = async (event) => {     result.preventDefault();   };    render (     <Form className="input-form" onSubmit={handleSubmit}>       <div className="col-md-6 offset-md-three"></div>     </Form>   ); };  export default ThirdStep;                  

In this file, nosotros're using a country-country-city npm library to go a list of available countries, cities, and states like this:

          import csc from 'country-land-city';                  

Then in the component, nosotros've divers some states:

          const [countries, setCountries] = useState([]); const [states, setStates] = useState([]); const [cities, setCities] = useState([]); const [isLoading, setIsLoading] = useState(false);  const [selectedCountry, setSelectedCountry] = useState(''); const [selectedState, setSelectedState] = useState(''); const [selectedCity, setSelectedCity] = useState('');                  

Hither, countries, states and cities are declared in the country that will shop the list of countries, states and cities, respectively, coming from the API.

We add together some other isLoading country to continue track of when the information is loading. selectedCountry, selectedState and selectedCity will incorporate the selected value when the user selects a particular dropdown value.

Then we've added a useEffect hook to make an API call to get the list of countries equally shown below:

          useEffect(() => {   ...   const result = wait csc.getAllCountries();   ... }, []);                  

Here, we're calling the getAllCountries method of the land-state-city library to get a listing of bachelor countries.

Annotation that nosotros've passed an empty array [] equally the 2d statement to the useEffect hook so the hook volition be called simply once when the component is mounted.

Now, open the SecondStep.js file and supervene upon the onSubmit function with the post-obit lawmaking:

          const onSubmit = (data) => {   console.log(data);   props.history.button('/third'); };                  

Using this lawmaking, nosotros can easily navigate to the ThirdStep component.

Now, let'due south check the application.

countries_log

As you can see, on the component load, we're getting a list of available countries in an assortment of objects.

Each object contains an isoCode and name belongings that we can utilise in our code to display it on the screen.

And so modify the useEffect hook to the beneath code:

          useEffect(() => {   const getCountries = async () => {     endeavour {       setIsLoading(true);       const event = await csc.getAllCountries();       let allCountries = [];       allCountries = result?.map(({ isoCode, proper name }) => ({         isoCode,         name       }));       const [{ isoCode: firstCountry } = {}] = allCountries;       setCountries(allCountries);       setSelectedCountry(firstCountry);       setIsLoading(false);     } catch (mistake) {       setCountries([]);       setIsLoading(false);     }   };    getCountries(); }, []);                  

Here, we're kickoff setting the isLoading flag to true to bespeak that information is loading, which we will utilise soon.

Each object of the array contains many other backdrop like phonecode, flag, currency and and then on, only we only want isoCode and name. So nosotros're using the array map method to filter out only those properties, equally shown below:

          allCountries = effect?.map(({ isoCode, name }) => ({   isoCode,   proper noun }));                  

Hither, nosotros're using the ES11 optional chaining operator which is denoted by the ?. The code after the ? will be executed merely if the previous reference is non undefined or aught. And as nosotros're destructuring isoCode and name, nosotros need the optional chaining operator.

The optional chaining operator is very useful in many scenarios. You lot tin can larn more most it in my Mastering Modern JavaScript book.

At present we have the post-obit code:

          const [{ isoCode: firstCountry } = {}] = allCountries; setCountries(allCountries); setSelectedCountry(firstCountry); setIsLoading(simulated);                  

Allow'southward understand what we're doing here.

Hither, we've used object destructuring renaming with assignment syntax. We're destructuring the isoCode property from the beginning object of the allCountries assortment of objects and renaming the isoCode property to firstCountry just to identify that it's the get-go country from the list.

We're also assigning a default empty object so that if the allCountries assortment is empty we won't get an fault.

In brusk, nosotros are saying to take the isoCode property from the offset object from the allCountries array of objects and rename it to firstCountry .

If the firstCountry property does not exist in the commencement object from the allCountries assortment, then assign a default value of empty object {} to the firstCountry variable.

Then we're updating the selectedCountry land value to the firstCountry value and isLoading state value to fake using the below code:

          setSelectedCountry(firstCountry); setIsLoading(false);                  

Now, in the ThirdStep.js file, change the post-obit code:

          render (   <Form className="input-grade" onSubmit={handleSubmit}>     <div className="col-md-half-dozen offset-md-iii"></div>   </Form> );                  

to this lawmaking:

          return (     <Form className="input-form" onSubmit={handleSubmit}>       <div className="col-doctor-6 offset-medico-3">         <Grade.Group controlId="country">           {isLoading && (             <p className="loading">Loading countries. Please wait...</p>           )}           <Grade.Label>Land</Form.Characterization>           <Course.Control             as="select"             name="country"             value={selectedCountry}             onChange={(consequence) => setSelectedCountry(outcome.target.value)}           >             {countries.map(({ isoCode, name }) => (               <option value={isoCode} primal={isoCode}>                 {name}               </option>             ))}           </Class.Command>         </Course.Group>       </div>     </Form>   );                  

We can see the list of countries populated in the dropdown.

Now, if yous navigate to footstep three, you will run into the following screen:

country_populate

As you tin can see, the land dropdown is correctly populated with all countries. On modify of the dropdown value, the selectedCountry state besides changes to the country lawmaking (isoCode) as you can come across in the React dev tools.

How to Get a List of States from the API

Now, allow'southward add the lawmaking for getting a list of states based on the selected country.

Add the post-obit code after the start useEffect hook in the ThirdStep.js file.

          useEffect(() => {     const getStates = async () => {       endeavour {         const result = await csc.getStatesOfCountry(selectedCountry);         permit allStates = [];         allStates = issue?.map(({ isoCode, name }) => ({           isoCode,           name         }));         console.log({ allStates });         const [{ isoCode: firstState = '' } = {}] = allStates;         setCities([]);         setSelectedCity('');         setStates(allStates);         setSelectedState(firstState);       } take hold of (error) {         setStates([]);         setCities([]);         setSelectedCity('');       }     };      getStates();   }, [selectedCountry]);                  

Here, nosotros're calling the getStatesOfCountry method from the country-state-city library by passing the selectedCountry every bit the parameter. Then based on the consequence of the API, nosotros're updating the corresponding states as shown beneath:

          setCities([]); setSelectedCity(''); setStates(allStates); setSelectedState(firstState);                  

All the country, land and urban center dropdowns are inter-related. If nosotros're changing the country, we should update the land too, which we're doing in the in a higher place code.

Also, annotation that we've passed the selectedCountry as a second parameter to the useEffect claw in the dependencies array:

          useEffect(() => {  ... }, [selectedCountry]);                  

So this result will just run when the selectedCountry land changes. This ways that once nosotros change the country dropdown, we're making an API call to get the states related to simply that country and so populating the land's dropdown values.

Now, add the following lawmaking subsequently the first Form.Group endmost tag that's later on the state dropdown:

          <Form.Group controlId="state">   <Form.Label>Land</Form.Label>   <Form.Control     as="select"     proper noun="state"     value={selectedState}     onChange={(event) => setSelectedState(outcome.target.value)}   >     {states.length > 0 ? (       states.map(({ isoCode, proper noun }) => (         <option value={isoCode} primal={isoCode}>           {name}         </option>       ))     ) : (       <option value="" key="">         No state found       </option>     )}   </Form.Control> </Form.Group>                  

Here, we're displaying the land dropdown on the screen. If at that place are no states for the selected land, we testify a No state institute bulletin because there are some countries that don't have whatsoever states.

Now, if you check the application, you volition run across the following screen:

state_populate

Every bit you lot can run into above, when we change the country dropdown value, the state dropdown list is likewise updated based on the selected country.

How to Get a Listing of Cities from the API

Now, permit's populate the cities based on the land and state values.

Add another useEffect hook after the second hook equally shown below:

          useEffect(() => {   const getCities = async () => {     endeavour {       const event = await csc.getCitiesOfState(         selectedCountry,         selectedState       );       let allCities = [];       allCities = result?.map(({ name }) => ({         proper name       }));       const [{ name: firstCity = '' } = {}] = allCities;       setCities(allCities);       setSelectedCity(firstCity);     } grab (error) {       setCities([]);     }   };    getCities(); }, [selectedState]);                  

Hither, we're calling the getCitiesOfState method from the country-land-city library past passing the selectedCountry and selectedState as parameters. Based on the outcome of the API, we update the cities dropdown.

Now, add together the following code after the second Grade.Group endmost tag that'southward after the state dropdown:

          <Grade.Group controlId="city">   <Form.Label>City</Form.Label>   <Class.Control     as="select"     proper noun="urban center"     value={selectedCity}     onChange={(event) => setSelectedCity(event.target.value)}   >     {cities.length > 0 ? (       cities.map(({ proper noun }) => (         <option value={name} key={proper noun}>           {proper name}         </option>       ))     ) : (       <option value="">No cities institute</option>     )}   </Form.Control> </Form.Group>                  

Hither, we're displaying the cities dropdown on the screen. If there are no cities for the selected land, we show a No cities found bulletin because there are some states that don't have any cities.

Now, if you lot check the awarding, you will see the following screen:

city_populate

Equally you tin can see above, on change of land and state, the corresponding list of cities is populated in the cities dropdown.

Also, add the Register button after the concluding Class.Grouping closing tag that's after the metropolis dropdown:

          <Push button variant="main" type="submit">   Register </Button>                  

Now, your screen will look like this:

last_step

We're done creating screens for all the steps. Now let'southward get the step progress in the header working so it's clear which step we're currently on.

We're showing the Progress component inside the Header component, but the Progress component is not mentioned in any of the Routedue south in the AppRouter.js file. Also, Header isn't mentioned in the Route.

So past default, we don't have admission to the history, location and match props in both the Header and Progress components to place which road we're on.

But there is an easy fashion to fix this. React Router provides a withRouter component which nosotros can employ in the Progress component so nosotros will get access to the history, location and match props.

Open up the Progress.js file and add together import the withRouter component at the tiptop of the file:

          import { withRouter } from 'react-router-dom';                  

and change the export statement from this code:

          export default Progress;                  

to this code:

          export default withRouter(Progress);                  

So when nosotros pass the Progress component to the withRouter component we'll get access to the history, location and friction match props inside the Progress component.

Now, replace the Progress component with the post-obit code:

          const Progress = ({ location: { pathname } }) => {   const isFirstStep = pathname === '/';   const isSecondStep = pathname === '/second';   const isThirdStep = pathname === '/tertiary';    return (     <React.Fragment>       <div className="steps">         <div className={`${isFirstStep ? 'stride agile' : 'step'}`}>           <div>1</div>           <div>             {isSecondStep || isThirdStep ? (               <Link to="/">Stride 1</Link>             ) : (               'Step i'             )}           </div>         </div>         <div className={`${isSecondStep ? 'step agile' : 'step'}`}>           <div>2</div>           <div>{isThirdStep ? <Link to="/2d">Footstep 2</Link> : 'Pace ii'}</div>         </div>         <div className={`${pathname === '/third' ? 'footstep active' : 'pace'}`}>           <div>3</div>           <div>Footstep iii</div>         </div>       </div>     </React.Fragment>   ); };                  

Here, in the first line, we're destructuring the location belongings from the props object and so the pathname belongings from the location holding in a single line like this:

          const Progress = ({ location: { pathname } }) => {                  

And based on which route we're on, we're adding the active class to each step div.

Also, import the Link component at the top of the file:

          import { Link, withRouter } from 'react-router-dom';                  

Now, if yous bank check the application, y'all volition see the post-obit screen:

progress_working

As you can see, when we're on a detail footstep, that footstep number is shown as agile in the progress bar with highlighted text. Then, as nosotros navigate through the steps, the text of the previous steps is shown every bit a link and so nosotros can navigate back to whatever step to change whatever data.

How to Retain Entered Information Beyond Routes

But you will notice that, when we become to step 1 by clicking the link from step three, the information entered in step 1 is lost.

This is because when we get from one route to another route, React Router completely unmounts the previous route component and mounts the next route component continued to that route. This causes all state values to be lost.

So permit's add together a way to preserve the data that's been entered when navigating to the previous pace.

Every bit you know, simply the components connected to the routes mentioned in the AppRouter.js file are mounted and unmounted on the route modify. But the AppRouter component in our case is not unmounted fifty-fifty when the routes alter.

This ways that the best place to store the information entered by the user is in the AppRouter component.

Let's add together the user state,updateUser, and resetUser functions within the AppRouter.js file.

          const [user, setUser] = useState({});  const updateUser = (data) => {   setUser((prevUser) => ({ ...prevUser, ...data })); };  const resetUser = () => {   setUser({}); };                  

So we will store the user-entered information in each step in the user state that'southward an object.

In the updateUser function, nosotros're passing data to update the user state. In the updateUser function, we're offset spreading out the user object values using the prevUser variable and then spreading out the data object so the resulting object volition be the merging of two objects.

To update the land, we use the updater syntax of state with implicit return syntax for the object.

Then this code:

          setUser((prevUser) => ({ ...prevUser, ...data }));                  

is the aforementioned as the below code:

          setUser((prevUser) => {   return {     ...prevUser,     ...data   }; });                  

As yous tin can see higher up, if we want to implicitly render an object from an arrow office, we can skip the render keyword and enclose the object in circular brackets.

This will make the lawmaking shorter and volition likewise help you avoid typing mistakes in your lawmaking. Considering of this, y'all will notice that most React lawmaking is written using implicit return syntax.

So if nosotros're in step 1 then we will pass the {first_name: 'Mike', last_name: 'Jordan' } as information and add it to the user land.

And then in stride 2, if nosotros pass {user_email: 'exam@instance.com', user_password: 'exam@123'} equally the data, then the updateUser function will update the user as shown beneath:

          const prevUser = { first_name: 'Mike', last_name: 'Jordan' }; const data = { user_email: 'test@example.com', user_password: 'test@123' };  const issue = { ...prevUser, ...information }; console.log(outcome); // { first_name: 'Mike', last_name: 'Jordan', user_email: 'test@case.com', user_password: 'test@123' }                  

Now, we have created the user state and updateUser function. And so we need to laissez passer it to each route that is continued to the step and then we can salvage the user-entered information by calling the updateUser office.

Our current routes in the AppRouter.js file wait like this:

          <Switch>   <Route component={FirstStep} path="/" exact={truthful} />   <Route component={SecondStep} path="/second" />   <Route component={ThirdStep} path="/third" /> </Switch>                  

So to laissez passer the user and updateUser as props to the components connected to the road, we can't pass it similar this:

          <Route component={FirstStep} path="/" exact={truthful} user={user} updateUser={updateUser} />                  

Considering this way props volition be passed to the Route and not to the FirstStep component. And so we need to use the following syntax:

          <Route   render={(props) => (     <FirstStep {...props} user={user} updateUser={updateUser} />   )}   path="/"   verbal={true} />                  

Here, we're using the render props pattern for passing props. This will correctly pass the props and volition likewise not re-create the FirstStep component on every re-render.

You can check out my Introduction to React Router grade to learn more than nigh why we demand to use return instead of the component prop.

Now, subsequently making this change for all the stride-related routes, your routes volition look similar this:

          <BrowserRouter>   <div className="container">     <Header />     <Switch>       <Road         render={(props) => (           <FirstStep {...props} user={user} updateUser={updateUser} />         )}         path="/"         verbal={truthful}       />       <Route         return={(props) => (           <SecondStep {...props} user={user} updateUser={updateUser} />         )}         path="/second"       />       <Route         render={(props) => (           <ThirdStep {...props} user={user}  />         )}         path="/third"       />     </Switch>   </div> </BrowserRouter>                  

Annotation that we're not passing the updateUser prop to the ThirdStep component route, because when we submit the form from step iii, nosotros will be saving all the data directly into the database.

If you desire you tin can pass the updateUser function to the ThirdStep component and save it to the state by calling the updateUser function (but information technology's not necessary).

Now, let's employ the updateUser role inside these components to save the information.

Then open the FirstStep.js and SecondStep.js files and inside the onSubmit handler role, and add props.updateUser(information) equally the starting time statement.

          // FirstStep.js const onSubmit = (data) => {   props.updateUser(data);   props.history.button('/2d'); };  // SecondStep.js const onSubmit = (data) => {   props.updateUser(information);   props.history.push('/third'); };                  

Now, if you check the awarding, you will run into the following screen:

saving_to_state

Equally you can come across, initially the AppRouter component country is an empty object. Simply when nosotros submit the form in each step, the country object gets updated with the user-entered data.

Now, let'due south utilise that data saved in the land and populate it in the respective input fields when nosotros come back to the previous step from the adjacent pace.

Equally you know, we're using react-hook-course to manage the changing data of our forms in the FirstStep and SecondStep component using the useForm hook.

But the useForm hook also takes an optional parameter which we can employ to persist the values beyond route changes.

Then change the below code from the FirstStep.js file:

          const { annals, handleSubmit, errors } = useForm();                  

to this code:

          const { user } = props; const { annals, handleSubmit, errors } = useForm({   defaultValues: {     first_name: user.first_name,     last_name: user.last_name   } });                  

Here, we're destructuring the user prop from the props object which we're passing in the route of the AppRouter.js file. Then nosotros're using the defaultValues property to set the value for each input field.

Just to remind yous, first_name and last_name are the names given to the input fields in FirstStep component which react-hook-grade uses to runway irresolute data.

Now, if you check the awarding, you volition see the post-obit screen:

data_retained

As you can see, when we come back from step 2 to step 1, the data entered in step i is not lost. This is because we're re-setting it with the data from the user state when the component is mounted again on route change.

At present, allow's add some similar code in the SecondStep.js file also:

          const { user } = props; const { register, handleSubmit, errors } = useForm({   defaultValues: {     user_email: user.user_email,     user_password: user.user_password   } });                  

If you check the application, you will see the following screen:

data_retained_step2

Equally you tin can see, when we come back from pace 3 to footstep 2 or step i, the data entered in step 1 and footstep two is not lost. And so nosotros have successfully preserved the information across steps.

How to Add Animated Transitions to the App

At present, permit's add a shine sliding animation functionality to the app.

To add blitheness, we're using the very popular framer movement library.

Framer motion makes it easy to add animation using a declarative approach in the same way that React does things.

And so let'due south add animation in the FirstStep component.

Open the FirstStep.js file and add the import statement for the framer motion library at the top of the file:

          import { motion } from 'framer-motion';                  

To breathing any chemical element on the page, nosotros demand to prefix information technology with motion like this:

          <div>Click here to animate it</div>  // the in a higher place code will need to exist converted to  <motion.div>Click here to animate it</move.div>                  

Using motion every bit a prefix will return a React component that has specific animating capabilities added so that we tin pass props to that element.

So inside the FirstStep.js file, after calculation the motion prefix to the post-obit div:

          <div className="col-md-6 offset-md-three"> ... </div>                  

it will look like this:

          <move.div className="col-medico-half dozen offset-md-3"> ... </motion.div>                  

Once nosotros add a motion prefix to information technology, nosotros can provide extra props to that element similar this:

          <motion.div   className="col-md-half-dozen kickoff-doc-three"   initial={{ 10: '-100vw' }}   breathing={{ x: 0 }} > ... </motion.div>                  

Hither, nosotros've provided an initial prop to specify the location from where the animation will begin. We desire the entire course to be slid in from the left side so nosotros provided the x value as -100vw. This means 100% viewport width from the left side. So the initial position of the form will be far left simply not visible on the screen.

And then we provided the animate prop with an x value of 0 so the form will slide in from left and will come up dorsum to its original position on the page. If we provide a value of 10 for ten so information technology will motion to 10px on the right side from its original position.

Now, your entire JSX code in the FirstStep.js file will look similar this:

          return (   <Form className="input-course" onSubmit={handleSubmit(onSubmit)}>     <motion.div       className="col-md-half-dozen offset-doc-iii"       initial={{ ten: '-100vw' }}       animate={{ ten: 0 }}     >       <Form.Grouping controlId="first_name">         <Form.Characterization>Starting time Proper name</Course.Characterization>         <Form.Control           type="text"           name="first_name"           placeholder="Enter your commencement proper name"           autoComplete="off"           ref={register({             required: 'First name is required.',             design: {               value: /^[a-zA-Z]+$/,               message: 'Starting time proper name should contain only characters.'             }           })}           className={`${errors.first_name ? 'input-error' : ''}`}         />         {errors.first_name && (           <p className="errorMsg">{errors.first_name.message}</p>         )}       </Form.Group>        <Grade.Grouping controlId="last_name">         <Form.Label>Concluding Name</Form.Label>         <Form.Control           type="text"           name="last_name"           placeholder="Enter your last name"           autoComplete="off"           ref={register({             required: 'Final name is required.',             pattern: {               value: /^[a-zA-Z]+$/,               message: 'Last name should comprise just characters.'             }           })}           className={`${errors.last_name ? 'input-error' : ''}`}         />         {errors.last_name && (           <p className="errorMsg">{errors.last_name.message}</p>         )}       </Form.Grouping>        <Push variant="primary" type="submit">         Next       </Button>     </motion.div>   </Class> );                  

At present, if you check the awarding, you volition run across the sliding animation on folio load:

sliding_animation

As yous can run across, the form slides in from the left side of the page but information technology does non yet await very polish.

To brand it a smooth animation, we can provide another transition prop in addition to the initial and breathing props.

          <motility.div   className="col-md-6 offset-doctor-3"   initial={{ 10: '-100vw' }}   breathing={{ x: 0 }}   transition={{ stiffness: 150 }} > ... </motility.div>                  

Here, nosotros've added a transition prop with value of 150 for stiffness. You lot can attempt changing the value from 150 to something else and check which 1 looks best to you. I volition use 150 hither.

Now, if you check the application, yous volition see a smoothen sliding animation on folio load:

smooth_animation

Let's brand the same animation changes in the SecondStep.js and ThirdStep.js files:

          import { motion } from 'framer-motility'; ... <motion.div   className="col-md-6 offset-dr.-3"   initial={{ x: '-100vw' }}   breathing={{ x: 0 }}   transition={{ stiffness: 150 }} > ... </motility.div>                  

Now if you check the application, y'all will see a smooth sliding animation on page load for all three steps:

all_steps_animation

How to Setup the Backend with Node.js

We're done with all the basic functionality for the forepart-end. Now let's setup the backend server code and then we tin can save the data entered in the grade to MongoDB.

Create a new binder with the name server outside the src folder. So create models and routers folders inside the server folder.

Now, execute the post-obit command from the server folder from the concluding:

          yarn init -y                  

This will create a packet.json file inside the server folder so nosotros can manage the dependencies.

Now, install the required dependencies by executing the following control from the server folder from terminal:

          yarn add bcryptjs@2.4.3 cors@ii.8.v express@4.17.one mongoose@five.eleven.18 nodemon@2.0.vii                  

Adjacent, create a new file with the name .gitignore inside the server folder and add together the post-obit line inside information technology and then the node_modules folder will not be pushed to GitHub (if you decide to push your lawmaking to GitHub):

          node_modules                  

Create a new file db.js inside the server binder with the following content:

          const mongoose = require('mongoose');  mongoose.connect('mongodb://127.0.0.1:27017/form-user', {   useNewUrlParser: true,   useCreateIndex: truthful,   useUnifiedTopology: true });                  

Here, nosotros're using the mongoose library to work with MongoDB. For the mongoose.connect method, nosotros've provided a connexion string with the course-user database every bit the proper noun of the database.

Y'all tin can requite whatever proper noun you want instead of form-user.

Now, create a new file with the name index.js inside the server folder and add the following contents inside it:

          const express = require('limited'); require('./db');  const app = express(); const PORT = procedure.env.PORT || 3030;  app.become('/', (req, res) => {   res.send('<h2>This is from index.js file</h2>'); });  app.listen(PORT, () => {   console.log(`server started on port ${PORT}`); });                  

Now, open the server/package.json file and add the scripts section inside it:

          "scripts": {   "beginning": "nodemon index.js" },                  

Hither we're using the nodemon npm package that volition restart the Express server if we make whatsoever changes in alphabetize.js or the files included in the index.js file. This manner nosotros don't have to manually restart the server on every alter.

So your entire package.json file will await like this:

          {   "proper name": "server",   "version": "1.0.0",   "main": "alphabetize.js",   "license": "MIT",   "scripts": {     "showtime": "nodemon alphabetize.js"   },   "dependencies": {     "bcryptjs": "two.4.3",     "cors": "ii.8.v",     "express": "four.17.ane",     "mongoose": "5.11.18",     "nodemon": "ii.0.7"   } }                  

Now, open another last and execute the yarn outset command from within the server folder.

If you access http://localhost:3030/, y'all will see the following screen:

server_initial_page

This shows that our Express server is correctly set. Allow's write Balance APIs to shop the user registration data.

Create a new file called user.js inside the server/models folder with the post-obit content:

          const mongoose = require('mongoose');  const userSchema = mongoose.Schema(   {     first_name: {       type: Cord,       required: true,       trim: true     },     last_name: {       type: Cord,       required: truthful,       trim: true     },     user_email: {       blazon: String,       required: true,       trim: true,       validate(value) {         if (!value.match(/^[^@ ]+@[^@ ]+\.[^@ .]{2,}$/)) {           throw new Error('Email is not valid.');         }       }     },     user_password: {       blazon: String,       required: true,       trim: true,       minlength: 6     },     country: {       blazon: String,       required: truthful,       trim: true     },     land: {       type: String,       trim: true     },     city: {       type: String,       trim: true     }   },   {     timestamps: true   } );  const User = mongoose.model('User', userSchema);  module.exports = User;                  

Here, nosotros've created a User schema to ascertain the structure of the data stored in the User collection.

If you accept never worked with the mongoose library and so cheque out this commodity for an introduction.

How to Create the Residuum APIs

Create a new file called user.js inside the routers folder with the following content:

          const express = crave('limited'); const User = require('../models/user'); const bcrypt = require('bcryptjs'); const router = express.Router();  router.post('/register', async (req, res) => {  const { user_email, user_password } = req.body;   console.log('req.body', req.body);   allow user = await User.findOne({ user_email });  if (user) {    return res.condition(400).send('User with the provided email already exist.');  }   try {    user = new User(req.body);    user.user_password = look bcrypt.hash(user_password, 8);     expect user.save();    res.condition(201).send();  } take hold of (e) {    res.status(500).send('Something went wrong. Attempt over again later.');  } });  module.exports = router;                  

Here, we've created a postal service API for the /register road. We volition be passing the information to this API in JSON format. The Express server makes information technology bachelor within the req.trunk object so we're destructuring the electronic mail and password value from information technology:

          const { user_email, user_password } = req.body;                  

Then using the findOne method of the User model, nosotros're outset checking if there is any user with the provided email address.

          permit user = await User.findOne({ user_email });                  

If such a user exists, and then we're returning an mistake dorsum to the client (which is our React app).

          return res.status(400).send('User with the provided email already be.');                  

Information technology's ever adept to specify the HTTP response lawmaking of the error while sending back the response.

You can find all HTTP condition codes and their meanings on this website.

Then we pass all the user data (like first_name, last_name, user_email, users_password, state, country and city) which is present in the req.body to the User constructor .

But we don't want to store the user-entered data into the database as it is. So we'll use the popular bcryptjs npm library to hash the password before saving it to the database.

          user.user_password = wait bcrypt.hash(user_password, 8);                  

Check out my article here to learn about bcryptjs in detail.

And one time the password is hashed, we call the save method of the User model to save all the details along with hashed password into the MongoDB database.

          await user.save();                  

Once we're done, we're sending dorsum the response with the status lawmaking of 201 which describes that something has been created.

          res.status(201).send();                  

Annotation that hither nosotros're non sending back whatever data – just a response saying that the asking was successful and a new record was created.

Then at the end, we're exporting the express router and so we can use information technology in the index.js file.

Now, open the server/alphabetize.js file and import the user router at the top of the file:

          const userRouter = require('./routers/user');                  

As nosotros're sending the information to annals from React app to Node.js server in JSON format, we demand to add together the following code for the middleware:

          app.utilise(express.json());                  

Also, after the PORT constant, add the following line of code:

          app.use(userRouter);                  

And then your entire server/alphabetize.js file will look like this:

          const express = require('express'); const userRouter = require('./routers/user'); require('./db');  const app = limited(); const PORT = process.env.PORT || 3030;  app.use(express.json()); app.use(userRouter);  app.get('/', (req, res) => {   res.send('<h2>This is from index.js file</h2>'); });  app.mind(PORT, () => {   console.log(`server started on port ${PORT}`); });                  

Here, nosotros've provided userRouter as a middleware for the Express app so that we can brand API requests to it.

Information technology's always good to separate out each router in its own file and include it using the app.use method. This avoids making the code larger past writing it in a unmarried file.

Now, first your local MongoDB database server by running ./mongod --dbpath=<path_to_mongodb-data_folder> as explained in this article and go on information technology running.

And so restart the Express server by running yarn beginning from the server binder and go on information technology running.

Open another terminal and offset the react app by running yarn first if yous oasis't already washed information technology.

So now you volition 2 separate terminals open up – one for running the Express server app and some other 1 running the React app as shown below:

vscode_terminals

Hither, we're opening terminals inside VSCode. You lot tin open the offset terminal by going to the Terminal -> New Terminal card in VS Lawmaking. So only click the + icon to open additional terminals.

How to Call REST APIs from a React App

Now, let's make the code changes in our React app to make the API telephone call to our /register API.

Open up the ThirdStep.js file and replace the handleSubmit method with the following code:

          const handleSubmit = async (event) => {     consequence.preventDefault();      endeavour {       const { user } = props;       const updatedData = {         country: countries.notice(           (country) => country.isoCode === selectedCountry         )?.name,         state:           states.find((state) => state.isoCode === selectedState)?.proper noun || '',         urban center: selectedCity       };        expect axios.post(`${BASE_API_URL}/register`, {         ...user,         ...updatedData       });     } catch (fault) {       if (mistake.response) {         console.log('fault', error.response.information);       }     }   };                  

Here, once we submit the form in step 2, we call the handleSubmit method where nosotros make an API call to our /register API:

          expect axios.mail(`${BASE_API_URL}/register`, {   ...user,   ...updatedData });                  

Here, we're passing the data to the /register API in the JSON format.

Nosotros store the land lawmaking in selectedCountry and the land lawmaking in selectedState state variables. These are denoted by isoCode, and we start use the assortment find method to find out the actual names related to that land and state lawmaking as shown below:

          const updatedData = {   land: countries.find(     (country) => country.isoCode === selectedCountry   )?.name,   state:     states.find((state) => state.isoCode === selectedState)?.name || '',   metropolis: selectedCity };                  

Within the selectedCity state variable we store the proper noun so we don't demand to use the filter method in that location.

If yous want a quick refresher on the most widely used assortment methods (including array find method) check out my commodity here.

While using the find method for state, we've added the || condition. This is because if there is no available country for any selected land, then while accessing ?.name, information technology might come as undefined. To avoid storing undefined in the database, we use the || operator to store an empty string '' instead or undefined.

How to Test REST APIs

At present, let's bank check the application'southward functionality.

cors_error

Equally you can see, when we try to submit the form in the stride 3, nosotros're getting a CORS (Cross-Origin Resource Sharing) mistake in the browser panel.

This is considering the browser does not allow the states to access the information of an application running on some other port – because we're running our React application on port 3000 and our Node.js application on port 3030.

This is for security reasons and violates cross-domain policies.

To fix this, nosotros need to install the cors npm package and use it in our server/index.js file so that the Node.js server will allow any application to access its APIs.

Don't worry, we volition run across how we tin can utilise the Node.js APIs without using cors later in this article. Nosotros'll as well avoid needing to run 2 split up terminals to get-go our React and Node.js server.

For at present, open the server/index.js file  and add the import for cors every bit shown below:

          const cors = require('cors');                  

Notation that nosotros've already installed the cors npm package while creating the Express server earlier.

And add together it as an Express middleware earlier the app.apply(userRouter) argument similar this:

          app.apply(express.json()); app.use(cors()); app.use(userRouter);                  

Now your index.js file will expect like this:

          const express = require('express'); const cors = require('cors'); const userRouter = crave('./routers/user'); crave('./db');  const app = express(); const PORT = procedure.env.PORT || 3030;  app.use(express.json()); app.use(cors()); app.apply(userRouter);  app.get('/', (req, res) => {   res.send('<h2>This is from alphabetize.js file</h2>'); });  app.heed(PORT, () => {   console.log(`server started on port ${PORT}`); });                  

If you submit the class, you will see that the information has been correctly logged into the console:

save_log-1

And the data besides gets saved into the database as shown below:

saved_into_db

So now, nosotros've successfully connected our front-end React app to the back-end Node.js app and saved the data to the database.

Y'all might take noticed that we're not showing any indication that the data has been successfully saved to the database once we annals the user. And then let's do that at present.

To bear witness the success message, nosotros'll employ sweetalert2 which is a popular and customizable popup modal library.

Import it in the ThirdStep.js file as shown below:

          import Swal from 'sweetalert2';                  

Inside the handleSubmit function, afterwards the axios.post call, add the following lawmaking in the effort cake:

          Swal.burn down('Awesome!', "You're successfully registered!", 'success').then( (event) => {   if (result.isConfirmed || effect.isDismissed) {     props.history.push('/');   } } );                  

and in the take hold of block add the following lawmaking:

          if (mistake.response) {   Swal.fire({     icon: 'error',     championship: 'Oops...',     text: mistake.response.data   }); }                  

And then your handleSubmit part will expect like this now:

          const handleSubmit = async (event) => {     result.preventDefault();      try {       const { user } = props;       const updatedData = {         country: countries.find(           (land) => country.isoCode === selectedCountry         )?.name,         state:           states.observe((state) => state.isoCode === selectedState)?.name || '', // or condition added because selectedState might come as undefined         city: selectedCity       };        await axios.mail(`${BASE_API_URL}/register`, {         ...user,         ...updatedData       });       Swal.fire('Awesome!', "Yous're successfully registered!", 'success').then(         (result) => {           if (result.isConfirmed || result.isDismissed) {             props.history.push('/');           }         }       );     } take hold of (error) {       if (fault.response) {         Swal.fire({           icon: 'error',           title: 'Oops...',           text: error.response.information         });         panel.log('mistake', mistake.response.information);       }     }   };                  

If y'all bank check the application, y'all will see the following screen:

email_error

Every bit you tin can see, if the user with the email address already exists in the database then we show an error message from the take hold of cake.

And if the user email does non be in the database, then we see the success popup every bit yous tin run into beneath:

success_register

If yous check the code of the popup for success, it looks like this:

          Swal.fire('Awesome!', "Y'all're successfully registered!", 'success').then(   (consequence) => {     if (result.isConfirmed || result.isDismissed) {       props.history.button('/');     }   } );                  

So if the user clicks on the OK button or clicks exterior the popup modal, we redirect the user to step ane using props.history.push('/');. Just we as well should articulate the user-entered data from the input fields once the registration is successful. Let'due south practise that at present.

If you remember, nosotros added a resetUser role within the AppRouter component to clear the user land data.

Let'due south pass this function as a prop to the ThirdStep component. So your ThirdStep route will look like this:

          <Route   return={(props) => (     <ThirdStep       {...props}       user={user}       updateUser={updateUser}       resetUser={resetUser}     />   )}   path="/third" />                  

And within the handleSubmit function of the ThirdStep.js file, earlier calling props.history.push('/'); call the resetUser function like this:

          Swal.fire('Awesome!', "You're successfully registered!", 'success').then(   (result) => {     if (result.isConfirmed || result.isDismissed) {       props.resetUser();       props.history.push('/');     }   } );                  

Now, if you annals a new user, you volition see that later registration, you volition be redirected to footstep ane and all the input fields will also be cleared.

cleared_fields

How to Add together Login Functionality to the App

We have added the entire registration functionality for the front-finish and dorsum-end. Let's add login functionality so we can check if a user with a provided electronic mail and password already exists and then call up the details of that user.

Open the routers/user.js file and add the following code inside it before the module.exports statement:

          router.mail service('/login', async (req, res) => {   try {     const user = await User.findOne({ user_email: req.body.user_email });     if (!user) {       return res.status(400).send('User with provided email does not exist.');     }      const isMatch = await bcrypt.compare(       req.body.user_password,       user.user_password     );      if (!isMatch) {       return res.condition(400).send('Invalid credentials.');     }     const { user_password, ...rest } = user.toObject();      return res.send(rest);   } grab (error) {     return res.status(500).send('Something went wrong. Try over again later.');   } });                  

Hither, we're first checking to see if the user with the provided email already exists using the findOne method. If no such user exists, then we return an error with a status lawmaking of 400.

If in that location is a user with the provided electronic mail address then we utilise the bcrypt.compare method to compare the original non-hashed password with the hashed countersign. If the hashed countersign does not match with the password from the user object, and so nosotros render an error saying Invalid credentials.

Merely if the password matches, then nosotros create a new rest object with all the user properties except the hashed countersign using the ES9 rest operator for objects:

          const { user_password, ...residual } = user.toObject();                  

This is because nosotros don't desire to return dorsum the hashed password for security reasons.

Then we will transport back the residue object with the password removed dorsum to the client (our React app).

Now that nosotros've created the back-finish API, let's integrate the front-end part for our login functionality.

Create a new file called Login.js inside the components folder with the following code:

          import React, { useState } from 'react'; import { useForm } from 'react-hook-form'; import { Course, Button } from 'react-bootstrap'; import axios from 'axios'; import { BASE_API_URL } from '../utils/constants';  const Login = () => {   const { register, handleSubmit, errors } = useForm();   const [successMessage, setSuccessMessage] = useState('');   const [errorMessage, setErrorMessage] = useState('');   const [userDetails, setUserDetails] = useState('');    const onSubmit = async (data) => {     console.log(data);      try {       const response = await axios.postal service(`${BASE_API_URL}/login`, data);       setSuccessMessage('User with the provided credentials plant.');       setErrorMessage('');       setUserDetails(response.data);     } take hold of (error) {       console.log(fault);       if (error.response) {         console.log('error', error.response.data);         setErrorMessage(error.response.information);       }     }   };    return (     <Form className="input-form" onSubmit={handleSubmit(onSubmit)}>       <div className="col-md-6 offset-md-3">         {errorMessage ? (           <p className="errorMsg login-error">{errorMessage}</p>         ) : (           <div>             <p className="successMsg">{successMessage}</p>              {userDetails && (               <div className="user-details">                 <p>Post-obit are the user details:</p>                 <div>First name: {userDetails.first_name}</div>                 <div>Concluding proper noun: {userDetails.last_name}</div>                 <div>Email: {userDetails.user_email}</div>                 <div>Country: {userDetails.state}</div>                 <div>State: {userDetails.country}</div>                 <div>City: {userDetails.city}</div>               </div>             )}           </div>         )}         <Form.Group controlId="first_name">           <Form.Label>Email</Grade.Label>           <Form.Control             type="email"             proper noun="user_email"             placeholder="Enter your email address"             ref={register({               required: 'Email is required.',               pattern: {                 value: /^[^@ ]+@[^@ ]+\.[^@ .]{two,}$/,                 message: 'E-mail is not valid.'               }             })}             className={`${errors.user_email ? 'input-error' : ''}`}           />           {errors.user_email && (             <p className="errorMsg">{errors.user_email.message}</p>           )}         </Form.Group>          <Course.Group controlId="countersign">           <Form.Label>Password</Form.Label>           <Form.Control             type="password"             name="user_password"             placeholder="Choose a password"             ref={register({               required: 'Countersign is required.',               minLength: {                 value: 6,                 message: 'Password should have at-least 6 characters.'               }             })}             className={`${errors.user_password ? 'input-error' : ''}`}           />           {errors.user_password && (             <p className="errorMsg">{errors.user_password.message}</p>           )}         </Form.Group>          <Push button variant="primary" type="submit">           Check Login         </Push>       </div>     </Class>   ); };  export default Login;                  

At present, open the AppRouter.js file and add a route for Login at the end of all routes earlier the ending Switch tag like this:

          <BrowserRouter>      ...     <Road component={Login} path="/login" />     </Switch>   </div> </BrowserRouter>                  

Also, include the Login component at the top:

          import Login from '../components/Login';                  

At present, if you access http://localhost:3000/login, you will see the following screen:

login_screen

Hither, we really don't need to show the steps in the header, so let'south add together a status to hibernate them on the login page.

Open the Progress.js file and add another const variable similar this:

          const isLoginPage = pathname === '/login';                  

And so add a ternary operator status before the starting time of the div with class steps:

          <React.Fragment>   {!isLoginPage ? (     <div className="steps">      ...     </div>   ) : (     <div></div>   )} </React.Fragment>                  

If the page is not a login page, and then nosotros'll brandish the steps – otherwise we will display an empty div.

Note that we need to return an empty div if we don't have anything to render, because React will throw an error if we don't return whatsoever JSX from the component.

Your entire Progress.js file volition await like this now:

          import React from 'react'; import { Link, withRouter } from 'react-router-dom';  const Progress = ({ location: { pathname } }) => {   const isFirstStep = pathname === '/';   const isSecondStep = pathname === '/second';   const isThirdStep = pathname === '/third';   const isLoginPage = pathname === '/login';    return (     <React.Fragment>       {!isLoginPage ? (         <div className="steps">           <div className={`${isFirstStep ? 'step active' : 'stride'}`}>             <div>1</div>             <div>               {isSecondStep || isThirdStep ? (                 <Link to="/">Step i</Link>               ) : (                 'Step i'               )}             </div>           </div>           <div className={`${isSecondStep ? 'footstep active' : 'stride'}`}>             <div>two</div>             <div>               {isThirdStep ? <Link to="/second">Step 2</Link> : 'Stride ii'}             </div>           </div>           <div className={`${pathname === '/third' ? 'pace agile' : 'stride'}`}>             <div>3</div>             <div>Step 3</div>           </div>         </div>       ) : (         <div></div>       )}     </React.Fragment>   ); };  export default withRouter(Progress);                  

How to Examination the Login Functionality

Now, if you lot bank check the login page, you will see the page without steps in the header. But the steps are still displayed for the other pages.

login_without_steps

And if you enter the correct login credentials then you will get the details related to that user equally shown below:

login_success_message

If the login credentials are invalid, you will see the error message every bit shown below:

invalid_login

If the email exists but the password does not match, so you volition see the error bulletin every bit shown below:

invalid_credentials

Now, let's sympathise the code from the Login.js file:

          const onSubmit = async (data) => {   console.log(information);    try {     const response = await axios.post(`${BASE_API_URL}/login`, information);     setSuccessMessage('User with the provided credentials found.');     setErrorMessage('');     setUserDetails(response.data);   } take hold of (error) {     console.log(error);     if (error.response) {       console.log('mistake', mistake.response.information);       setErrorMessage(mistake.response.data);     }   } };                  

In the onSubmit role, nosotros're making an API call to the /login endpoint past passing the data entered in the login form.

If there is no mistake in the API response, we volition set the successMessage country and gear up the userDetails land with the response from the API. Otherwise nosotros will set the errorMessage state.

And in the JSX, if the errorMessage state is not empty, nosotros'll display the fault message otherwise brandish successMessage state value with the userDetails data:

          {errorMessage ? (   <p className="errorMsg login-error">{errorMessage}</p> ) : (   <div>     <p className="successMsg">{successMessage}</p>      {userDetails && (       <div className="user-details">         <p>Following are the user details:</p>         <div>First name: {userDetails.first_name}</div>         <div>Last name: {userDetails.last_name}</div>         <div>Email: {userDetails.user_email}</div>         <div>Land: {userDetails.country}</div>         <div>State: {userDetails.land}</div>         <div>Urban center: {userDetails.city}</div>       </div>     )}   </div> )}                  

Note that we accept non provided a link for the login folio on the screen because the application is meant to brandish multi-pace grade functionality. I take included the login page so you can get an thought of how to validate user login.

If you want, you can include the login page link in the header or directly admission it using http://localhost:3000/login.

How to Setup an Invalid Route Folio

Now, we're washed with the unabridged functionality of the App. Let'due south add some code then that if we enter any invalid route in the browser URL, the user will be redirected dorsum to the dwelling page.

Currently, if you access any invalid road like http://localhost:3000/contact, yous will run across a blank folio. Just at that place is as well no fault in the console because there is no matching route in the list of routes inside the AppRouter.js file.

blank_page-1

Open the AppRouter.js file, and subsequently the login route enter another route as shown below:

                      ...   <Route component={Login} path="/login" />   <Route render={() => <Redirect to="/" />} /> </Switch>                  

Here, we haven't provided whatsoever path to the Route component for the last Road. This ways that if whatever of the in a higher place routes practise not lucifer, this last Route will be executed. This will redirect the user to the / Route which is the FirstPage component road.

Also, import the Redirect component from the react-router-dom at the top of the file:

          import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';                  

Note that you lot need to enter it as the last route only. This mode if any of the above routes practice not match, the concluding route volition exist executed and information technology will redirect to the home page.

Let's verify it now.

page_not_found

As you can come across, for all invalid routes we're redirected to the dwelling house page which is the starting time step folio.

How to Get Rid of the CORS Library

Every bit you know, to run this awarding, we demand to start our React app using the yarn start control in i terminal. We also need to execute the yarn get-go command from the server binder for the backend server. And finally, we also need to keep our MongoDB server running in the third terminal.

So let's remove the demand to run two separate yarn start commands. This will also allow you lot to deploy your app on a single hosting provider.

If yous call back, in the server/index.js file, nosotros added the post-obit code:

          app.employ(cors());                  

Adding this code allows any awarding to access our APIs – which is fine when working in a local surroundings. But it'due south non safety to permit anybody to access our APIs. And so let's fix that.

Open up the server/alphabetize.js file and add the below lawmaking but in a higher place the app.use(express.json()); line:

          app.use(express.static(path.join(__dirname, '..', 'build')));                  

Hither, we're configuring our Limited app to use the contents of the build binder as a starting point of our app.

The build folder will be created when we run the yarn build control for our React app.

As the build binder will be created exterior the server folder, we're using .. to come up out of the server binder to admission information technology.

Besides, import the path Node package at the top of the file:

          const path = require('path');                  

We don't need to install the path npm parcel, as it'due south added by default when we install Node.js on our system.

At present, you can remove the cors import and its employ from the server/index.js file.

Your final server/index.js file will expect like this:

          const path = require('path'); const limited = crave('express'); const userRouter = crave('./routers/user'); require('./db');  const app = express(); const PORT = procedure.env.PORT || 3030;  app.employ(express.static(path.join(__dirname, '..', 'build'))); app.utilise(express.json()); app.use(userRouter);  app.get('/', (req, res) => {  res.send('<h2>This is from index.js file</h2>'); });  app.listen(PORT, () => {  console.log(`server started on port ${PORT}`); });                  

Now stop both the yarn start commands from both the terminals. Then, only in one concluding execute the yarn build control from inside the multi-step-grade-using-mern folder which is our project folder.

The yarn build command will have some time to complete as it performs some optimizations. It should merely be executed when we're done with all the app functionality and when we're gear up to deploy the app to product.

build_completed

Once the command completes successfully, you lot will come across a build binder created as shown below:

file_structure

The build folder contains our entire React app so you tin apply this build binder to deploy your app to production.

Now, open the src/utils/constants.js file and supercede this code:

          export const BASE_API_URL = 'http://localhost:3030';                  

with the below code:

          export const BASE_API_URL = '';                  

At present, as we've created the build folder, navigate to server binder from the terminal and execute the yarn start command:

server_started

As y'all can see, the server has started on port 3030.

And so permit's access our application at http://localhost:3030/.

complete_flow

Every bit y'all can run into, we only demand to run one yarn start command to outset the Node.js Express server. The Node.js server renders our React app on port 3030 from the build folder.

And then all our APIs are available now on http://localhost:3030 such as http://localhost:3030/register and http://localhost:3030/login.

Therefore nosotros accept changed the BASE_API_URL value to but an empty string:

          consign const BASE_API_URL = '';                  

When we're already on http://localhost:3030 we tin can make all our Post request APIs using only /login and /register.

So we only demand ane final to run the yarn commencement command and another terminal for starting the MongoDB service. This means that nosotros can deploy our app on single hosting provider like heroku instead of deploying the React app on one hosting provider and the Node.js app on another hosting provider.

Note that if you make any changes to the React app's code, you lot volition need to re-run the yarn build command from the projection folder and then yarn start command from the server folder.

Simply there is ane event with this setup. If y'all straight go to any road apart from the / route like /beginning, /second, /login and and then on, you will become an fault as you'll see below:

error

This is because we're starting the server from Limited.js and so the asking will always go to the Express server (our Node server was created using Limited) and at that place is no /second route for handling that on the Node side. And so it gives us an mistake.

To set up this, open the server/alphabetize.js file and add together the following code before the app.listen argument and after all other routes:

          app.use((req, res, side by side) => {   res.sendFile(path.join(__dirname, '..', 'build', 'index.html')); });                  

This code will act as a default route. If any of the previous routes exercise not match, this code will send dorsum the index.html file from the build folder which is our React app.

And considering the /2d route is present in our React app, you will see the right stride two page.

If the entered route is not present on the Node.js app equally well as in our React app, and then the user volition be redirected to the step 1 folio (our habitation page) because of our last route in the AppRouter.js file.

          <Route return={() => <Redirect to="/" />} />                  

At this point, your consummate server/index.js file will look similar this:

          const path = require('path'); const express = require('express'); const userRouter = require('./routers/user'); require('./db');  const app = express(); const PORT = process.env.PORT || 3030;  app.use(express.static(path.join(__dirname, '..', 'build'))); app.use(express.json()); app.use(userRouter);  app.get('/', (req, res) => {   res.ship('<h2>This is from index.js file</h2>'); });  app.utilise((req, res, next) => {   res.sendFile(path.join(__dirname, '..', 'build', 'index.html')); });  app.mind(PORT, () => {   console.log(`server started on port ${PORT}`); });                  

And you will not become an fault now:

error_fixed

If y'all want to learn in-depth about rendering React apps using Node.js, check out this article.

Now we're done with both the forepart-end and back-end functionality equally you can come across below:

complete_working_app

Closing points

We're done edifice out the functionality of the App.

You can find the complete GitHub source code for this application in this repository.

To have your skills further, you can improve the application past adding an extra validation at footstep 3 to bank check if the user has entered all the details in the class. This is important because you can directly visit the 2d footstep page of the class by using http://localhost:3030/2nd and proceed from there.

Thanks for reading!

Want to learn all ES6+ features in particular including allow and const, promises, various promise methods, assortment and object destructuring, arrow functions, async/await, import and export and a whole lot more from scratch?

Check out my Mastering Modernistic JavaScript book. This book covers all the pre-requisites for learning React and helps you to become better at JavaScript and React.

Likewise, yous can bank check out my free Introduction to React Router course to learn React Router from scratch.

Want to stay up to date with regular content regarding JavaScript, React, and Node.js? Follow me on LinkedIn.

Learn to code for free. freeCodeCamp'south open source curriculum has helped more than forty,000 people go jobs as developers. Go started

Source: https://www.freecodecamp.org/news/build-a-multi-step-registration-app-with-animated-transitions-using-mern-stack/

Posted by: tayloragpich.blogspot.com

0 Response to "How To Animate Sign In Process React"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel