javascriptreactjsdatereact-hook-formreal-time-updates

How to update a field in React Hook Form based on other fields' values?


I'm using React Hook Form with three input fields:

I want the night difference to update automatically whenever the start or end date changes.

Is there a way to update the night difference without using onChange, but by placing the logic inside register?

Codesandbox

import "./styles.css";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, getValues, setValue } = useForm();

  return (
    <div className="App">
      <form>
        <label>Start Date</label>
        <input
          type="date"
          id="startDate"
          {...register("startDate", {
            required: "This field is required",
          })}
          onChange={(e) =>
            setValue(
              "numNights",
              Math.floor(
                (new Date(getValues().endDate) - new Date(e.target.value)) /
                  (1000 * 60 * 60 * 24)
              ) || ""
            )
          }
        />

        <label>End Date</label>
        <input
          type="date"
          id="endDate"
          {...register("endDate", {
            required: "This field is required",
            validate: (value) => {
              if (value <= getValues().startDate) {
                return "End date must be after the start date";
              }
            },
          })}
          onChange={(e) =>
            setValue(
              "numNights",
              Math.floor(
                (new Date(e.target.value) - new Date(getValues().startDate)) /
                  (1000 * 60 * 60 * 24)
              )
            )
          }
        />

        <label>Nights</label>
        <input type="text" id="numNights" readOnly {...register("numNights")} />
      </form>
    </div>
  );
}

Solution

  • You can use useWatch to observe changes in the date fields.

    By using useEffect, you can then calculate the difference in days (nights) whenever either the start or end date is changed.

    Simplified solution:

    Codesandbox Demo

    import { useForm, useWatch } from "react-hook-form";
    import { useEffect } from "react";
    
    export default function App() {
      const { register, getValues, setValue, control } = useForm();
    
      const startDate = useWatch({ control, name: "startDate" });
      const endDate = useWatch({ control, name: "endDate" });
    
      const calculateNights = (start, end) => {
        if (start && end) {
          const diffDays = Math.floor(
            (new Date(end) - new Date(start)) / (1000 * 60 * 60 * 24)
          );
          return diffDays > 0 ? diffDays : "";
        }
        return "";
      };
    
      useEffect(() => {
        setValue("numNights", calculateNights(startDate, endDate));
      }, [startDate, endDate, setValue]);
    
      return (
        <form>
          <label>Start Date</label>
          <input type="date" {...register("startDate", { required: true })} />
    
          <label>End Date</label>
          <input type="date" {...register("endDate", { required: true })} />
    
          <label>Nights</label>
          <input type="text" readOnly {...register("numNights")} />
        </form>
      );
    }