The problem arises because the validate function in react-hook-form does not handle asynchronous operations well when combined with state updates from react-query. As a result, the hasErrors value does not get updated immediately due to the asynchronous nature of React state updates. To address this issue, we can use a useEffect hook to manage the form errors based on the react-query state.

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useQuery } from 'react-query';
import './index.css';

export function App() {
  const {
    register,
    setError,
    clearErrors,
    formState: { errors },
    getValues,
  } = useForm({
    mode: 'onChange',
  });

  const { data, refetch, isError, isRefetchError, isLoadingError } = useQuery(
    'repoData',
    () => fetch(getValues('example')).then((res) => res.json()),
    {
      enabled: false,
      refetchOnMount: false,
      refetchOnReconnect: false,
      refetchOnWindowFocus: false,
      retry: false,
    }
  );

  const hasErrors = isError || isRefetchError || isLoadingError;

  const validate = async (value: string) => {
    if (value.length > 3) {
      await refetch();

      // The validation function here does not return any validation result because errors are handled via useEffect
      return true;
    }

    return true;
  };

  useEffect(() => {
    const value = getValues('example');
    if (value && value.length > 3) {
      if (hasErrors) {
        setError('example', {
          type: 'manual',
          message: `Validation hasErrors value: ${JSON.stringify(hasErrors)}`,
        });
      } else {
        clearErrors('example');
      }
    }
  }, [hasErrors, setError, clearErrors, getValues]);

  return (
    <>
      <h1>Input Validation Bug</h1>
      <form>
        <input
          defaultValue="https://jsonplaceholder.typicode.com/todos/1"
          {...register('example', { validate })}
          placeholder="Enter any public API URL"
        />
        {errors.example && <p>{errors.example?.message}</p>}
        <span>
          Actual hasErrors value: <b>{JSON.stringify(hasErrors)}</b>
        </span>
      </form>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </>
  );
}

 

Support On Demand!

ReactJS