skip to Main Content

I have a multi-page react-hook-form form and I’m running into a validation issue I can’t seem to wrap my head around. I’m trying to only validate the page the user is on, not pages they haven’t made it to yet.

Here’s a CodeSandbox

If you enter something into the input and click "Next", you can move on to the next page. If you click "Back" and then try to click "Next" again, it won’t let you because it’s validating the second page but the user isn’t on that page.

I should be able to freely go between page 1 and 2 even though I haven’t added an item to my field array. It should only validate if I try to click "Next" on the 2nd page.

2

Answers


  1. Solution

    You should be using separate forms for each page and collect all the different forms’ data using either a 3rd party library for this or some custom-defined higher state. This is the recommended approach by the official react-hook-form docs, see Advanced Usage > Wizard Form / Funnel.

    Explanation of the problem itself

    The problem you’re having is that when you click "Next", it invokes handleSubmit which validates the data prior to invoking the onSubmit callback (see docs).

    This is not a problem when you first load the page with PageOne, as it includes only a required input field that you populate prior to clicking "Next" for the first time, but it becomes a problem once PageTwo loads – it add a required items field to the form which is invalid by default until you add an actual item to it.

    You should only ever use handleSubmit if you intend to validate and submit the all of the form data; you should not use it to open/load another part of the same form (emphasis on "same").

    Login or Signup to reply.
  2. I ran into a similar problem and solved it by making each page its own form. Since I needed to submit all the pages together, the submit action on each form saved the data to a ref and then the last page actually submitted the data to my API.

    It looked something like this:

    MultiForm.jsx

    const [pageIndex, setPageIndex] = useState(0)
    const savedData = useRef([])
    
    const storeData = (data) => {
      // Save or rewrite data on this page
      savedData.current[pageIndex] = data
    }
    
    const handleNext = (data) => {
      storeData(data)
      setPage(page + 1)
    }
    const handleBack = (data) => {
      storeData(data)
      setPage(page - 1)
    }
    const handleSubmit = (data) => {
      storeData(data)
      onSubmit(savedData.current) // handle your form submit
    }
    
    return (
      <>
        {
          page === 0 && (
            <PageOne 
              onNext={handleNext}
              defaultValues={savedData[0]}
            />
          )
        }
        {
          page === 1 && (
            <PageTwo
              onBack={handleBack}
              onSubmit={handleSubmit}
              defaultValues={savedData[1]}
            />
          )
        }
      </>
    )
    
    

    PageOne.jsx

    const { onNext, defaultValues } = props
    const { register, handleSubmit } = useForm()
    
    
    return (
      <form onSubmit={handleSubmit(onNext)}>
        <input 
          type="text"
          {...register("name", { required: true })}
          defaultValue={defaultValues?.name || ''}  
        />
        <input 
          type="text"
          {...register("email")}
          defaultValue={defaultValues?.email || ''}  
        />
        <input type="submit" />
      </form>
    )
    

    PageTwo.jsx

    const { onBack, onSubmit, defaultValues } = props
    const { register, handleSubmit } = useForm()
    
    
    return (
      <form onSubmit={handleSubmit(onSubmit)}>
        <textarea
          {...register("description", { required: true })}
          defaultValue={defaultValues?.description || ''}  
        />
        <button type="button" onClick={onBack}>Back</button>
        <input type="submit" />
      </form>
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search