Form validation with conditional fields using yup

1.8k views Asked by At

I am trying to conditional render the fields and submit the data.

But on the below code snippet, i am not able to achieve this. Issues are re rendering happens and the values not getting persisting.

Here mostly the problem is i am splitting the schema, so based on the selection of the choice the field renders.

How can i submit the data with proper persisting the validation and values.

What i have tried so far

App.js

import React, { useEffect, useState } from "react";
import "./styles.css";
import Select from "react-select";
import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers";
import { CITIZEN_OPTIONS, validationSchema } from "./util";
import { Resident, NonResident } from "./CitizenDetails";
const Fallback = () => null;

export default function App() {
  const [citizenValue, setCitizenValue] = useState(null);

  const { register, control, errors, watch, handleSubmit } = useForm({
    resolver: yupResolver(validationSchema),
    mode: "onBlur",
    reValidateMode: "onChange"
  });

  const watchCitizen = watch("citizen");

  useEffect(() => {
    setCitizenValue(watchCitizen?.value);
  }, [watchCitizen]);

  const citizenDetails = {
    resident: () => <Resident errors={errors} register={register} />,
    non_resident: () => (
      <NonResident errors={errors} control={control} register={register} />
    )
  };

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

  const SelectedCitizenFields = citizenDetails[citizenValue] || Fallback;

  return (
    <div className="App">
      <Controller
        as={Select}
        control={control}
        name="citizen"
        options={CITIZEN_OPTIONS}
      ></Controller>
      <SelectedCitizenFields />
      <button onClick={handleSubmit(onSubmit)}>Submit Details</button>
    </div>
  );
}

CitizenDetails.js

import React, { Fragment, memo } from "react";
import { Controller } from "react-hook-form";
import Select from "react-select";

export const Resident = memo((props) => {
  const { errors, register } = props;
  return (
    <Fragment>
      <div className="field-group">
        <label>Enter first name</label>
        <input ref={register} name="first_name"></input>
        {errors?.first_name?.message && (
          <span>{errors.first_name.message}</span>
        )}
      </div>
      <div className="field-group">
        <label>Enter last name</label>
        <input ref={register} name="last_name"></input>
        {errors?.last_name?.message && <span>{errors.last_name.message}</span>}
      </div>
    </Fragment>
  );
});

export const NonResident = memo((props) => {
  const { errors, register, control } = props;
  return (
    <Fragment>
      <div className="field-group">
        <label>Enter passport number</label>
        <input ref={register} name="passport_number"></input>
        {errors?.passport_number?.message && (
          <span>{errors.passport_number.message}</span>
        )}
      </div>
      <div className="field-group">
        <label>Choose Country</label>
        <Controller as={Select} control={control} name="country" options={[]} />
        {errors?.country?.message && <span>{errors.country.message}</span>}
      </div>
      <div className="field-group">
        <label>Choose State</label>
        <Controller as={Select} control={control} name="state" options={[]} />
        {errors?.state?.message && <span>{errors.state.message}</span>}
      </div>
    </Fragment>
  );
});

util.js

import { object, string, number, lazy } from "yup";

export const CITIZEN_OPTIONS = [
  {
    label: "Resident",
    value: "resident"
  },
  {
    label: "Non-Resident",
    value: "non_resident"
  }
];

const required = () => "field is required";

const resident_schema = object({
  first_name: string().required(required).nullable(),
  last_name: string().required(required).nullable()
});

const non_resident_schema = object({
  passport_number: string().required(required).nullable(),
  country: object().required(required).nullable(),
  state: object().required(required).nullable()
});

export const validationSchema = lazy(({ citizen }) => {
  return citizen?.value === "resident" ? resident_schema : non_resident_schema;
});

Here is the codesandbox link

0

There are 0 answers