react-hook-form only registers the last step's form data in multi-level form

1.2k views Asked by At

I built a multilevel form using this css-tricks article and I am trying to validate the form using react-hook-form, but the react-hook-form only sees the step3 inputs data, and I need all the input data from step1 to step3 so I can validate and send to the server. It is a simple registration form for a web app.

import React, { useState } from "react"
import { Link } from "gatsby"
import { useForm } from "react-hook-form"
import { FormTitle } from "../components/input"
import { StepOne, StepTwo, StepThree } from "../components/joinFormSteps"

export default function () {
  const [currentStep, setCurrentStep] = useState(1)
  const { register, handleSubmit } = useForm()

  const _next = () => {
    // If the current step is 1 or 2, then add one on "next" button click
    setCurrentStep(currentStep >= 2 ? 3 : currentStep + 1)
  }

  const _prev = () => {
    // If the current step is 2 or 3, then subtract one on "previous" button click
    setCurrentStep(currentStep <= 1 ? 1 : currentStep - 1)
  }

  const previousButton = () => {
    if (currentStep !== 1) {
      return (
        <button className={styles.join__nxtbutton} type="button" onClick={_prev}>
          Previous
        </button>
      )
    }
    // ...else return nothing
    return null
  }

  const nextButton = () => {
    if (currentStep < 3) {
      return (
        <button
          className={`${styles.join__nxtbutton} ${
            currentStep === 1 ? styles.join__btnMarginRightZero : ""
          } `}
          type="button"
          onClick={_next}
        >
          Next
        </button>
      )
    }

    if (currentStep === 3) {
      return (
        <input
          className={`${styles.join__nxtbutton} ${
            currentStep === 1 ? styles.join__btnMarginRightZero : ""
          } `}
          type="submit"
          name="submit"
          value="Join Now"
        />
      )
    }
    // ...else render nothing
    return null
  }

  const onSubmit = data => {
    console.log(data)
  }

  return (
    <div className={styles.join}>
      <FormTitle title="Become a member" />

      <form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
        <StepOne currentStep={currentStep} register={register} />
        <StepTwo currentStep={currentStep} register={register} />
        <StepThree currentStep={currentStep} register={register} />

        <div className={styles.join__buttonArea}>
          <Link to="/sign-in" className={styles.join__link}>
            Sign in instead
          </Link>
          <div>
            {previousButton()}
            {nextButton()}
          </div>
        </div>
      </form>
    </div>
  )
}

Implementation

import React from "react"
import { Input, SelectInput, CheckboxInput } from "../components/input"
import states from "../assets/nigeria-states.json"
import styles from "./joinFormSteps.module.scss"

export function StepOne({ currentStep, register }) {
  if (currentStep !== 1) {
    return null
  }

  return (
    <React.Fragment>
      <div className={styles.form__row}>
        <div className={styles.form__input}>
          <Input
            label="Firstname*"
            name="firstname"
            type="text"
            placeholder="Firstname"
            register={register}
          />
        </div>

        <div className={styles.form__input}>
          <Input
            label="Lastname*"
            name="lastname"
            type="text"
            placeholder="Lastname"
            register={register}
          />
        </div>
      </div>

      <div className={styles.form__input}>
        <Input
          label="Email*"
          name="email"
          type="email"
          placeholder="[email protected]"
          register={register}
        />
      </div>

      <div className={styles.form__row}>
        <div className={styles.form__input}>
          <Input
            label="Password*"
            name="password"
            type="password"
            placeholder="Password"
            register={register}
          />
        </div>
        <div className={styles.form__input}>
          <Input
            label="Confirm Password*"
            name="confPassword"
            type="password"
            placeholder="Confirm Password"
            register={register}
          />
        </div>
      </div>
    </React.Fragment>
  )
}

export function StepTwo({ currentStep, register }) {
  if (currentStep !== 2) {
    return null
  }

  return (
    <React.Fragment>
      <div className={styles.form__row}>
        <div className={styles.form__input}>
          <Input
            label="Address*"
            name="address"
            type="text"
            placeholder=""
            register={register}
          />
        </div>

        <div className={styles.form__input}>
          <Input label="City*" name="city" type="text" placeholder="" register={register} />
        </div>
      </div>

      <div className={styles.form__row}>
        <div className={styles.form__input}>
          <SelectInput label="State*" name="state" options={states} register={register} />
        </div>

        <div className={styles.form__input}>
          <Input
            label="Postal code"
            name="postalCode"
            type="text"
            placeholder=""
            register={register}
          />
        </div>
      </div>

      <div className={styles.form__input}>
        <Input
          label="Phone Number*"
          name="number"
          type="number"
          placeholder="Phone Number"
          register={register}
        />
      </div>
    </React.Fragment>
  )
}

export function StepThree({ currentStep, register }) {
  if (currentStep !== 3) {
    return null
  }

  return (
    <React.Fragment>
      <div className={styles.form__input}>
        <SelectInput
          label="Membership type*"
          name="memberType"
          options={[
            { code: 1, name: "Associate Membership" },
            { code: 2, name: "Full Membership" },
          ]}
          register={register}
        />
      </div>

      <div className={styles.form__input}>
        <CheckboxInput
          label="How do you identify yourself*"
          options={[
            { name: "author", value: "An Author" },
            { name: "illustrator", value: "An Illustrator" },
          ]}
          register={register}
        />
      </div>
    </React.Fragment>
  )
}
1

There are 1 answers

0
NearHuscarl On BEST ANSWER

The problem here is in this code below:

export function StepOne({ currentStep, register }) {
  if (currentStep !== 1) {
    return null
  }

  return (...);
}


export function StepTwo({ currentStep, register }) {
  if (currentStep !== 2) {
    return null
  }

  return (...);
}


export function StepThree({ currentStep, register }) {
  if (currentStep !== 3) {
    return null
  }

  return (...);
}

Once you press the next button, you go to the next step. StepOne component then returns null which make the children components unmounted and unregister your fields.

To solve this issue, you must not unmount the components when jumping between steps. You can do that by hiding the component when you don't want to display it. So try changing your code to this:

export function StepOne({ currentStep, register }) {
  return (
    <div key={1} style={currentStep !== 1 ? { display: "none" } : {}}>
      ...
    </div>
  );
}


export function StepTwo({ currentStep, register }) {
  return (
    <div key={2} style={currentStep !== 2 ? { display: "none" } : {}}>
      ...
    </div>
  );
}


export function StepThree({ currentStep, register }) {
  return (
    <div key={3} style={currentStep !== 3 ? { display: "none" } : {}}>
      ...
    </div>
  );
}

Live Demo

Edit 64183942/react-hook-form-only-registers-the-step-three-form-data-in-multi-level-form