react-hook-form - Controller not updating on first change

Viewed 5255

This is my example https://codesandbox.io/s/react-hook-form-basic-forked-4stdl?file=/src/index.js

It has 2 input examples - firstName and lastName. Typing a space into firstName correctly displays an error.

Typing a space into lastName (Using a Controller) does not display the error until second keypress. It does not fire on first change.

Not sure why this is.

import React, { FC } from "react";
import { useFormContext, useWatch, Controller } from "react-hook-form";

export interface Props {
  name: string;
  label?: string;
  defaultValue?: string;
}

const ControlledInput: FC<Props> = ({
  name,
  label: labelText = "",
  defaultValue = ""
}) => {
  const { register, control, errors } = useFormContext();

  const label = labelText || name;
  const value = useWatch({
    control,
    name,
    defaultValue
  });

  console.log(
    `ControlledInput() render(), name=${name}, defaultvalue="${defaultValue}", value="${value}"`
  );
  return (
    <div>
      <Controller
        control={control}
        name={name}
        defaultValue={defaultValue}
        render={(props) => {
          const errorText = errors[name]?.type;

          // console.log('#################################');
          // console.log('name = ', name);
          // console.log(`errors[${name}] = `, errors[name]);
          // console.log('type = ', type);
          // console.log('errorText =', errorText);

          return (
            <>
              <input
                name={props.name}
                defaultValue={defaultValue}
                ref={register({
                  validate: {
                    beginSpace: (value) => /^\S/.test(value)
                  }
                })}
                type={"text"}
                placeholder={label}
                aria-label={label}
                data-error={errorText !== "" && errorText}
              />
              {errorText ? (
                <span style={{ color: "white" }}>
                  {label} ERROR: {errorText}
                </span>
              ) : null}
            </>
          );
        }}
      />
    </div>
  );
};

export default ControlledInput;
2 Answers

I don't really know why exactly, but it updates correctly on first change if you put your validating rules on the Controller directly in the rules prop instead of giving them to the register of the contained input.

Here is the codesandbox.

And here is that part of the code:

      <Controller
        control={control}
        name={name}
        defaultValue={defaultValue}
        rules={{
          validate: {
            beginSpace: (value) => /^\S/.test(value)
          }
        }}
        render={(props) => {
          const errorText = errors[name]?.type;
          return (
            <>
              <input
                name={props.name}
                defaultValue={defaultValue}
                ref={register}
                type={"text"}
                placeholder={label}
                aria-label={label}
                data-error={errorText !== "" && errorText}
              />
              {errorText ? (
                <span style={{ color: "white" }}>
                  {label} ERROR: {errorText}
                </span>
              ) : null}
            </>
          );
        }}
      />

you can use useEffect with dependency like that:

`

const { errors, dirtyFields } = form.formState
useEffect(() => {
    if (
        !Object.keys(errors).length &&
        dirtyFields.password &&
        dirtyFields.account &&
        dirtyFields.full_name &&
        dirtyFields.user_name
    ) {
        setActiveButtonRegister(true);
    } else {
        setActiveButtonRegister(false);
    }
}, [dirtyFields, errors]);`
Related