React Hook 表单验证不会阻止使用 useController 自定义钩子提交表单

问题描述 投票:0回答:1

我在项目中使用 React Hook Form 来处理表单验证。我有一个自定义钩子 useFormField ,它与 React Hook Form 中的 useController 集成。但是,我遇到了一个问题,即使不满足验证规则,也不会阻止表单提交。

相关代码如下:

自定义挂钩(useFormField):

import { useController, useFormContext, RegisterOptions, FieldValues, FieldPath, UseControllerProps } from 'react-hook-form';

type UseFormFieldProps<TFieldValues extends FieldValues> = {
  name: FieldPath<TFieldValues>;
  defaultValue?: any;
  rules?: Omit<RegisterOptions<TFieldValues, FieldPath<TFieldValues>>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
};

export function useFormField<TFieldValues extends FieldValues>({ name, defaultValue, rules }: UseFormFieldProps<TFieldValues>) {
  const { control } = useFormContext<TFieldValues>();

  const { field, fieldState: { error, isTouched, isDirty }, formState: { isSubmitting } } = useController({
    name,
    control,
    defaultValue,
    rules,
  } as UseControllerProps<TFieldValues>);

  return {
    ...field,
    error,
    isTouched,
    isDirty,
    isSubmitting,
  };
}

测试组件:

import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { FormProvider, useForm } from 'react-hook-form';
import { useFormField } from '@/hooks/useFormField';

function TestComponent({ name, defaultValue, rules, onSubmit = (d) => {} }: { name: string, defaultValue?: any, rules?: any, onSubmit?: (data: any) => void }) {
  const methods = useForm({ mode: 'onChange' });

  const { value, onChange, onBlur, error, isTouched, isDirty, isSubmitting } = useFormField({ name, defaultValue, rules });

  return (
    <form onSubmit={methods.handleSubmit(onSubmit)} data-testid="form">
      <input type="text" value={value || ''} onChange={onChange} onBlur={onBlur} data-testid="input" />
      {error && <span data-testid="error">{error.message}</span>}
      <span data-testid="touched">{isTouched ? 'touched' : 'untouched'}</span>
      <span data-testid="dirty">{isDirty ? 'dirty' : 'clean'}</span>
      <span data-testid="submitting">{isSubmitting ? 'submitting' : 'not submitting'}</span>
      <button type="submit" data-testid="submit">Submit</button>
    </form>
  );
}

function Wrapper({ children }: { children: React.ReactNode }) {
  const methods = useForm({ mode: 'onChange' });
  return <FormProvider {...methods}>{children}</FormProvider>;
}

测试用例:

it('should apply validation rules on submit', async () => {
  const onSubmitMock = jest.fn();
  render(
    <Wrapper>
      <TestComponent name="test" rules={{ required: 'This field is required' }} onSubmit={onSubmitMock} />
    </Wrapper>
  );

  const input = screen.getByTestId('input');
  const submitButton = screen.getByTestId('submit');

  // Submit with empty field
  fireEvent.click(submitButton);

  await waitFor(() => {
    expect(onSubmitMock).not.toHaveBeenCalled();
    const errorElement = screen.queryByTestId('error');
    expect(errorElement).not.toBeNull();
    expect(errorElement).toHaveTextContent('This field is required');
  });

  // Fill in the field and submit again
  fireEvent.change(input, { target: { value: 'valid input' } });
  fireEvent.click(submitButton);

  await waitFor(() => {
    expect(onSubmitMock).toHaveBeenCalledWith(expect.objectContaining({ test: 'valid input' }));
    expect(screen.queryByTestId('error')).toBeNull();
  });
});

尽管定义了验证规则,当表单提交为空时,onSubmit 函数仍然被调用。预期的行为是应阻止表单提交并显示验证错误。

什么可能导致即使验证失败,表单提交仍继续进行?如何确保在不满足验证规则时正确阻止表单提交?

reactjs frontend react-hook-form
1个回答
0
投票

在 TestComponent 中,您再次调用 useForm,这会创建两个单独的表单定义。自定义挂钩中的控制值来自上下文,而其他值来自 TestComponent 中新定义的表单。您应该改用 Wrapper 组件中的上下文。

function TestComponent({
  name,
  defaultValue,
  rules,
  onSubmit = (d) => {},
}: {
  name: string;
  defaultValue?: any;
  rules?: any;
  onSubmit?: (data: any) => void;
}) {
  const methods = useFormContext<FieldValues>(); // this line

  const { value, onChange, onBlur, error, isTouched, isDirty, isSubmitting } =
    useFormField({ name, defaultValue, rules });

  return (
    <form onSubmit={methods.handleSubmit(onSubmit)} data-testid="form">
      <input
        type="text"
        value={value || ''}
        onChange={onChange}
        onBlur={onBlur}
        data-testid="input"
      />
      {error && <div data-testid="error">{error.message}</div>}
      <div data-testid="touched">{isTouched ? 'touched' : 'untouched'}</div>
      <div data-testid="dirty">{isDirty ? 'dirty' : 'clean'}</div>
      <div data-testid="submitting">
        {isSubmitting ? 'submitting' : 'not submitting'}
      </div>
      <button type="submit" data-testid="submit">
        Submit
      </button>
    </form>
  );
}

function Wrapper({ children }: { children: React.ReactNode }) {
  const methods = useForm({ mode: 'onChange' });
  return <FormProvider {...methods}>{children}</FormProvider>;
}

确保在 TestComponent 中使用 useFormContext 来访问正确的表单上下文,确保控件和其他状态值在组件之间保持一致。

© www.soinside.com 2019 - 2024. All rights reserved.