0
votes

I have a problem with react-hook-form whereby simply "touching" a field (clicking into it then clicking elsewhere) is causing the formState.isDirty to be true. I've reproduced the problem in this codesandbox:

https://codesandbox.io/s/react-hook-form-dirty-by-touch-p8x6j

I'm loading a record from the server and calling reset() so that it's regarded as the new baseline, and this is probably the cause of the problem. But, being a dynamic form, I need to retrieve the object form the server to load into the form, so I see no way around this. All I want is for the formState.isDirty to remain false as long as the values don't actually change.

I don't have this problem with more complex components (such as react-select) for which I'm just calling register('whatever') myself, and it's only the native HTML inputs where I'm using ref={register} that are having this problem.

import React, { useState, useEffect } from "react";
import "./styles.css";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

export default function App() {
  interface MyObject {
    testing: string;
    id: number;
  }

  const blankObject: MyObject = {
    testing: "Default value",
    id: 0
  };

  const [obj, setObj] = useState(blankObject);

  const schema = yup.object().shape({
    testing: yup.string().max(40)
  });

  const { register, handleSubmit, formState, reset, errors } = useForm<
    MyObject
  >({
    resolver: yupResolver(schema),
    defaultValues: blankObject
  });

  useEffect(() => {
    setTimeout(() => {
      const newObj = {
        testing: "Pretend this came from the server",
        id: 123
      };
      setObj(newObj);
      reset(newObj);
    }, 2000);
  }, [reset]);

  const onSubmit = (data: MyObject) => {
    alert(JSON.stringify(data));
  };

  const onReset = () => {
    reset();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {obj.id === 0 ? (
        <p>Please wait...</p>
      ) : (
        <div>
          <p>ID: {obj.id}</p>
          <label>Enter text: </label>
          <input
            type="text"
            name="testing"
            ref={register}
            autoComplete="off"
            style={{ width: "50%" }}
            defaultValue={obj.testing}
          />
          {errors.testing && (
            <div style={{ color: "#ff0000" }}>{errors.testing?.message}</div>
          )}

          <pre>{JSON.stringify(formState, null, 2)}</pre>

          <input type="submit" />
          <input type="button" value="reset" onClick={onReset} />
        </div>
      )}
    </form>
  );
}

I could probably get rid of the ref={register} and handle the registration manually, but it seems a bit beside the point of react-hook-form to do so.

Thanks.

1

1 Answers

1
votes

How React hook form verifies a form is dirty or not is based on deep compare defaultValues.

here are your example's defaultValues: (after the reset)

const newObj = {
  testing: "Pretend this came from the server",
  id: 123
};

however, there is no such field as id got registered at hook form. so the comparison will also be a mismatch because id is missing.

The following example may give you a better context.

https://codesandbox.io/s/snowy-bush-jjcqk?file=/src/App.tsx