How do you make required fields optional in zod based on whether the content/control is rendered or not?

Hello,

I am using Zod with useForm for form validation. Some of the controls are displayed on the page based on radio button question? Here is my code

import { Button, HStack } from “@chakra-ui/react”;
import { z } from “zod”;
import { useForm } from “react-hook-form”;
import { zodResolver } from “@hookform/resolvers/zod”;
import useWorkItems from “…/…/hooks/UserConsole/useWorkItems”;
import workItemStore from “…/…/workItemStore”;
import React, { ChangeEvent, useState } from “react”;

const schema = z.object({
workType: z.string().min(1, { message: “Please select work type” }),
workSubType: z.string().min(1, { message: “Please select work sub type” }),
workTypeDescriptions: z.string().min(1, { message: “Please select description” }),
radioButtonSelection: z.enum([“Yes”, “No”], {errorMap: () => ({ message: “Please select an option” })}),
policyNumber: z.string().optional().refine((value, data) => {
if ( data && data.radioButtonSelection === “Yes”) {
return value && value.length >= 7;
}
return true;
}, {message: ‘please enter policy number’,path:[‘policyNumber’]}),
});

type WorkItemFormData = z.infer;

const workTypes = [
{ id: 1, description: “worktype 1” },
{ id: 2, description: “worktype 2” },
{ id: 3, description: “worktype 3” },
];
const workSubTypes = [
{ id: 1, description: “workSubtype 1”, workTypeId: 1 },
{ id: 2, description: “workSubtype 2”, workTypeId: 2 },
{ id: 3, description: “workSubtype 3”, workTypeId: 3 },
];
const workTypeDescriptions = [
{ id: 1, description: “worktypeDesc 1”, workSubTypeId: 1 },
{ id: 2, description: “worktypeDesc 2”, workSubTypeId: 2 },
{ id: 3, description: “worktypeDesc 3”, workSubTypeId: 3 },
];

const AddNew = () => {
const data = useWorkItems();
const initializeWorkItems = workItemStore((s) => s.InitializeWorkItems);
const [selectedOption, setSelectedOption] = useState<“Yes” | “No”>();

const handleRadioChange = (event: ChangeEvent) => {
const selectedValue = event.target.value === “Yes” ? “Yes” : “No”;
setSelectedOption(selectedValue);
setValue(“radioButtonSelection”, selectedValue);
setError(“radioButtonSelection”, { message: “” });
};

const {
register,
handleSubmit,
formState: { errors },
setValue,
setError,
reset,
} = useForm({ resolver: zodResolver(schema) });

return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<div style={{ paddingBottom: “20px” }}>

<label style={{ width: “150px”, textAlign: “right” }}>Bulk Add
<button
onClick={() => {
initializeWorkItems(data.results);
console.log(data.results);
}}
className=“btn btn-secondary”
style={{ borderRadius: “10px” }}
>
Add Items






<label
htmlFor=“workType”
className=“form-label”
style={{ width: “150px”, textAlign: “right” }}
>
Work Type

<select
{…register(“workType”)}
id=“workType”
className=“form-select w-25”
>
<option value={“”}>
{workTypes.map((workType) => (
{workType.description}
))}

        {errors.workType && (
          <p className="text-danger">{errors.workType.message}</p>
        )}
      </HStack>
    </div>
    <div className="mb-3">
      <HStack>
        <label
          htmlFor="workSubType"
          className="form-label"
          style={{ width: "150px", textAlign: "right" }}
        >
          Work Sub Type
        </label>
        <select
          {...register("workSubType")}
          id="workSubType"
          className="form-select w-25"
        >
          <option value={""}></option>
          {workSubTypes.map((workType) => (
            <option key={workType.id}>{workType.description}</option>
          ))}
        </select>
        {errors.workSubType && (
          <p className="text-danger">{errors.workSubType.message}</p>
        )}
      </HStack>
    </div>
    <div className="mb-3">
      <HStack>
        <label
          htmlFor="workTypeDescriptions"
          className="form-label"
          style={{ width: "150px", textAlign: "right" }}
        >
          Description
        </label>
        <select
          {...register("workTypeDescriptions")}
          id="workTypeDescriptions"
          className="form-select w-50"
        >
          <option value={""}></option>
          {workTypeDescriptions.map((workTypeDescription) => (
            <option key={workTypeDescription.id}>
              {workTypeDescription.description}
            </option>
          ))}
        </select>
        {errors.workTypeDescriptions && (
          <p className="text-danger">
            {errors.workTypeDescriptions.message}
          </p>
        )}
      </HStack>
    </div>
    <div>
      <HStack style={{ paddingLeft: "70px" }}>
        <label className="form-label">
          Does this work item have a policy?
        </label>
        <div style={{ padding: "0 10px" }}>
          <input
            {...register("radioButtonSelection")}
            id="havePolicy"
            type="radio"
            value="Yes"
            name="YesNoRadio"
            checked={selectedOption === "Yes"}
            onChange={handleRadioChange}
          />
          <label
            htmlFor="havePolicy"
            style={{ padding: "5px 5px 10px 5px" }}
          >
            Yes
          </label>
          <input
            {...register("radioButtonSelection")}
            id="haveNoPolicy"
            type="radio"
            value="No"
            name="YesNoRadio"
            checked={selectedOption === "No"}
            onChange={handleRadioChange}
          />
          <label
            htmlFor="haveNoPolicy"
            style={{ padding: "5px 0 10px 5px" }}
          >
            No
          </label>
        </div>
        {errors.radioButtonSelection && (
          <p className="text-danger">
            {errors.radioButtonSelection.message}
          </p>
        )}
      </HStack>
    </div>
    {selectedOption === "Yes" && (
      <div>
        <div className="mb-3">
          <HStack>
            <label
              htmlFor="policyNumber"
              className="form-label"
              style={{ width: "150px", textAlign: "right" }}
            >
              Policy Number
            </label>
            <input
              {...register("policyNumber")}
              id="policyNumber"
              type="text"
              style={{ width: "200px" }}
              className="form-control"
            />
            <button
              className="btn btn-primary"
              style={{ borderRadius: "20px", padding: "5px 15px 5px 15px" }}
            >
              Verify
            </button>
            {errors.policyNumber && (
              <p className="text-danger">{errors.policyNumber.message}</p>
            )}
          </HStack>
        </div>
        <div className="mb-3">
          <HStack>
            <label
              htmlFor="region"
              className="form-label"
              style={{ width: "150px", textAlign: "right" }}
            >
              Region
            </label>
            <input
              id="region"
              type="text"
              style={{ width: "50px" }}
              className="form-control"
              readOnly
            />

            <label
              htmlFor="state"
              className="form-label"
              style={{ width: "150px", textAlign: "right" }}
            >
              State
            </label>
            <input
              id="state"
              type="text"
              style={{ width: "50px" }}
              className="form-control"
              readOnly
            />
          </HStack>
        </div>
        <div className="mb-3">
          <HStack>
            <label
              htmlFor="policyHolderName"
              className="form-label"
              style={{ width: "150px", textAlign: "right" }}
            >
              Policy Holder(s)
            </label>
            <input
              id="policyHolderName"
              type="text"
              readOnly
              style={{ width: "275px" }}
              className="form-control"
            />
          </HStack>
        </div>
        <div className="mb-3">
          <HStack>
            <label
              htmlFor="company"
              className="form-label"
              style={{ width: "150px", textAlign: "right" }}
            >
              Company
            </label>
            <input
              id="company"
              type="text"
              readOnly
              className="form-control"
              style={{ width: "50px" }}
            />
          </HStack>
        </div>
      </div>
    )}
    <HStack style={{ paddingLeft: "160px" }}>
      <button
        className="btn btn-primary"
        style={{ borderRadius: "20px", padding: "5px 15px 5px 15px" }}
      >
        Add
      </button>
      <button
        className="btn btn-secondary"
        style={{ borderRadius: "20px", padding: "5px 15px 5px 15px" }}
        onClick={() => reset}
      >
        Reset
      </button>
    </HStack>
  </div>
</form>

);
};

export default AddNew;