我有一个结构如下的向导表单:
{
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
?
诀窍是使用
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);
};