Multi-Step Forms with State Management

Loading

Multi-step forms are used to break a long form into smaller sections, improving user experience by displaying fewer inputs at a time. React provides a flexible way to create multi-step forms with state management. You can control the form’s state by maintaining it across multiple steps, allowing users to navigate between form sections seamlessly.

In this guide, we will cover how to implement multi-step forms in React using different state management approaches such as useState, useReducer, and libraries like Formik or React Hook Form.


1. Basic Multi-Step Form with useState

The most basic way to manage multi-step forms in React is using the useState hook to track both the form data and the current step.

Example: Multi-Step Form Using useState

import React, { useState } from 'react';

const MultiStepForm = () => {
  const [step, setStep] = useState(1);
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    address: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  const handleNext = () => {
    setStep(step + 1);
  };

  const handlePrev = () => {
    setStep(step - 1);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      {step === 1 && (
        <div>
          <label>
            Name:
            <input
              type="text"
              name="name"
              value={formData.name}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handleNext}>
            Next
          </button>
        </div>
      )}

      {step === 2 && (
        <div>
          <label>
            Email:
            <input
              type="email"
              name="email"
              value={formData.email}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handlePrev}>
            Previous
          </button>
          <button type="button" onClick={handleNext}>
            Next
          </button>
        </div>
      )}

      {step === 3 && (
        <div>
          <label>
            Address:
            <input
              type="text"
              name="address"
              value={formData.address}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handlePrev}>
            Previous
          </button>
          <button type="submit">Submit</button>
        </div>
      )}
    </form>
  );
};

export default MultiStepForm;

Explanation:

  • Step Control: The step state variable is used to keep track of which step the user is on.
  • Form Data: The formData state holds the values of the form fields and updates as the user navigates through the steps.
  • Navigation: The “Next” and “Previous” buttons control the navigation between the steps.
  • Form Submission: Once the user reaches the final step and clicks “Submit,” the form data is logged to the console.

2. Multi-Step Form with useReducer

useReducer can be an alternative to useState when the state logic becomes more complex, such as managing multiple steps in a form.

Example: Multi-Step Form Using useReducer

import React, { useReducer } from 'react';

const initialState = {
  step: 1,
  formData: {
    name: '',
    email: '',
    address: '',
  },
};

function reducer(state, action) {
  switch (action.type) {
    case 'SET_FIELD':
      return {
        ...state,
        formData: {
          ...state.formData,
          [action.field]: action.value,
        },
      };
    case 'NEXT_STEP':
      return {
        ...state,
        step: state.step + 1,
      };
    case 'PREV_STEP':
      return {
        ...state,
        step: state.step - 1,
      };
    default:
      return state;
  }
}

const MultiStepFormWithReducer = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleChange = (e) => {
    dispatch({
      type: 'SET_FIELD',
      field: e.target.name,
      value: e.target.value,
    });
  };

  const handleNext = () => {
    dispatch({ type: 'NEXT_STEP' });
  };

  const handlePrev = () => {
    dispatch({ type: 'PREV_STEP' });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', state.formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      {state.step === 1 && (
        <div>
          <label>
            Name:
            <input
              type="text"
              name="name"
              value={state.formData.name}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handleNext}>
            Next
          </button>
        </div>
      )}

      {state.step === 2 && (
        <div>
          <label>
            Email:
            <input
              type="email"
              name="email"
              value={state.formData.email}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handlePrev}>
            Previous
          </button>
          <button type="button" onClick={handleNext}>
            Next
          </button>
        </div>
      )}

      {state.step === 3 && (
        <div>
          <label>
            Address:
            <input
              type="text"
              name="address"
              value={state.formData.address}
              onChange={handleChange}
            />
          </label>
          <button type="button" onClick={handlePrev}>
            Previous
          </button>
          <button type="submit">Submit</button>
        </div>
      )}
    </form>
  );
};

export default MultiStepFormWithReducer;

Explanation:

  • useReducer: The useReducer hook is used to handle both the form data and step transitions. The reducer function manages state updates, such as moving to the next step or setting the value of form fields.
  • Dispatch: The dispatch function sends actions to update the state, like changing the current step or updating form values.

3. Multi-Step Form with Formik

Formik simplifies form handling, especially for multi-step forms, as it handles validation, state management, and form submission. Here’s how to use Formik to build a multi-step form.

Example: Multi-Step Form with Formik

import React from 'react';
import { Formik, Form, Field } from 'formik';

const MultiStepFormikForm = () => {
  return (
    <Formik
      initialValues={{ name: '', email: '', address: '' }}
      onSubmit={(values) => {
        console.log('Form submitted with values:', values);
      }}
    >
      {({ values, setFieldValue }) => (
        <Form>
          <div>
            <label>Name:</label>
            <Field type="text" name="name" value={values.name} />
          </div>

          <div>
            <label>Email:</label>
            <Field type="email" name="email" value={values.email} />
          </div>

          <div>
            <label>Address:</label>
            <Field type="text" name="address" value={values.address} />
          </div>

          <button type="submit">Submit</button>
        </Form>
      )}
    </Formik>
  );
};

export default MultiStepFormikForm;

Explanation:

  • Formik: Formik handles all the form state management, so you don’t need to manually handle the input fields or form submission.
  • Form: We can add a multi-step feature by showing specific steps based on user interaction and state changes.

4. Multi-Step Form with React Hook Form

React Hook Form is another library that is lightweight and optimized for performance. You can manage multi-step forms with React Hook Form by breaking the form into individual steps.

Example: Multi-Step Form with React Hook Form

import React from 'react';
import { useForm, Controller } from 'react-hook-form';

const MultiStepRHFForm = () => {
  const { control, handleSubmit, watch } = useForm({
    defaultValues: {
      name: '',
      email: '',
      address: '',
    },
  });

  const onSubmit = (data) => {
    console.log('Form submitted with data:', data);
  };

  const step = watch('step');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {step === 1 && (
        <div>
          <label>Name:</label>
          <Controller
            name="name"
            control={control}
            render={({ field }) => <input {...field} />}
          />
          <button type="button" onClick={() => setValue('step', 2)}>Next</button>
        </div>
      )}

      {step === 2 && (
        <div>
          <label>Email:</label>
          <Controller
            name="email"
            control={control}
            render={({ field }) => <input {...field} />}
          />
          <button type="button" onClick={() => setValue('step', 1)}>Previous</button>
          <button type="button" onClick={() => setValue('step', 3)}>Next</button>
        </div>
      )}

      {step === 3 && (
        <div>
          <label>Address:</label>
          <Controller
            name="address"
            control={control}
            render={({ field }) => <input {...field} />}
          />
          <button type="button" onClick={() => setValue('step', 2)}>Previous</button>
          <button type="submit">Submit</button>
        </div>
      )}
    </form>
  );
};

export default MultiStepRHFForm;

Explanation:

  • useForm: The useForm hook from React Hook Form is used to handle form state management.
  • Controller: The Controller component is used to wrap input fields and register them with the form state.
  • Step Navigation: The step is tracked using the watch function, and users can navigate between steps.

Leave a Reply

Your email address will not be published. Required fields are marked *