如何以多步形式验证部分?

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

我有一个结构如下的向导表单:

{
  contactInfo:{
    name: "string",
    email: "string",
    ...
  }
  petInfo: {
    petName: "string",
    petBreed: "string,
    ...
  }
  vetInfo: {
    vetName: "string",
    vetPhone: "string",
    ...
  }
}

在组件的顶层,我渲染每个部分:

// Form Validation
import { yupResolver } from "@hookform/resolvers/yup";
import schema from "utils/FormValidation/wizardFormValidation";
import { defaultValues } from "utils/FormValidation/packFormValidation";

function getSteps(): string[] {
  return ["Contact", "Emergency", "Pack", "Vet"];
}

function getStepContent(stepIndex: number): JSX.Element {
  switch (stepIndex) {
    case 0:
      return <Contact />;
    case 1:
      return <Emergency />;
    case 2:
      return <Pack />;
    case 3:
      return <Vet />;
    default:
      return null;
  }
}

function Welcome(): JSX.Element {
  const dispatch = useAppDispatch();
  const petParent = useAppSelector(selectPetParent);
  const pack = useAppSelector(selectPack);
  const wizard = useAppSelector((state) => state.forms.wizard);
  const values = {
    ...petParent,
    pack: pack.length !== 0 ? [...pack] : [...defaultValues],
  } as Wizard;
  const [activeStep, setActiveStep] = useState<number>(0);
  const steps = getSteps();
  const isLastStep: boolean = activeStep === steps.length - 1;

  const handleNext = () => setActiveStep(activeStep + 1);
  const handleBack = () => setActiveStep(activeStep - 1);

  const methods = useForm({
    mode: "all",
    resolver: yupResolver(schema),
    defaultValues: {
      ...wizard,
    },
    values: values,
  });

  const onSubmit = () => {
    const formValues: Wizard = methods.getValues() as unknown as Wizard;
    dispatch(submitWizardForm(formValues)).unwrap();
  };

  const handleDisabled = (): boolean => {
    let section: any;
    switch (activeStep) {
      case 0:
        section = "contactInfo";
        break;
      case 1:
        section = "eContacts";
        break;
      case 2:
        section = "pack";
        break;
      case 3:
        section = "vetInfo";
        break;
    }
    const fieldState = methods.getFieldState(section, methods.formState);
    return activeStep !== 3 ? fieldState.invalid : !methods.formState.isValid;
  };

  useEffect(() => {
    const subscription = methods.watch((value, { name, type }) => {
      dispatch(updateWizardForm(value));
    });
    return () => subscription.unsubscribe();
  }, [methods.watch]);

  return (
    <FormProvider {...methods}>
      <div role="form" onSubmit={methods.handleSubmit(onSubmit)}>
        <div>{getStepContent(activeStep)}</div>
        {activeStep === 0 ? (
          <div></div>
        ) : (
          <button onClick={handleBack}>back</button>
        )}

        <button
          disabled={handleDisabled()}
          onClick={!isLastStep ? handleNext : onSubmit}
        >
          {isLastStep ? "submit" : "next"}
        </button>
      </div>
    </FormProvider>
  );
}

export default Welcome;

我遇到的这个问题是确定每个部分(contactInfo、petInfo、vetInfo)是否

isValid
,以便正确处理是否禁用
next
按钮。目前
handleDisabled
检查该部分是否为
fieldState.invalid
,但如果表单的部分为空白,则这并不总是返回 false。我尝试检查
fieldState.isTouched
fieldState.isDirty
部分,但它们不会检查该部分中的每个字段,而只是检查整个部分。意味着如果用户触摸/弄脏部分中的单个字段
isTouched
/
isDirty
返回 true,而
getFieldState.invalid
返回 false,这会导致用户不知道他们需要填写哪些字段。

formState
上的方法是理想的,但它们仅适用于整个表单。我想检查是否有特定部分
isValid
。否则,我必须在每个部分中使用
getFieldState
检查每个单独的字段。

我已经查看了文档,但我看到的唯一解决方案是将每个部分分解为单独的表单,然后在每个单独的表单部分上使用

formState.isValid
。有没有办法保持代码原样并逐节检查
isValid

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

诀窍是使用

trigger()
来验证
handleNext

上的整个表单
  const handleNext = async () => {
    let section: any;
    switch (activeStep) {
      case 0:
        section = "contactInfo";
        break;
      case 1:
        section = "eContacts";
        break;
      case 2:
        section = "pack";
        break;
      case 3:
        section = "vetInfo";
        break;
      default:
        section = "contactInfo";
    }
    const isStepValid = await trigger(section);
    if (isStepValid) setActiveStep((prevActiveStep) => prevActiveStep + 1);
  };
© www.soinside.com 2019 - 2024. All rights reserved.