如何在Formik父组件中获取子组件错误对象以标记校验区域?
实现父组件获取Formik表单错误对象的方案
方法一:通过回调函数实时同步错误对象
这种方式可以在表单错误变化时(输入校验、提交校验)实时将错误传递给父组件,适合实时标记未校验区域。
1. 父组件修改
添加状态存储错误对象,并定义接收错误的回调函数:
import { useState } from 'react'; // ...父组件其他逻辑 const [formErrors, setFormErrors] = useState({}); // 接收子组件传递的错误对象 const handleFormErrors = (errors) => { setFormErrors(errors); };
将回调函数传递给FormContainer组件:
<FormContainer initialFieldValues={id != undefined ? cvRecord : cvFormFields} formData={getFieldsByCategory(cvFormFields, selectOption.type)} formValidation={cvFormFields} onFormSubmit={onFormSubmit} onErrorsChange={handleFormErrors} {/* 新增回调props */} > {/* 按钮组 */} <div className="text-md-end mt-5"> <button className="btn btn-primary ms-2 mb-2" type="submit"> Save <i className="bi bi-file-earmark-fill" /> </button> <button type="button" className="btn btn-danger ms-2 mb-2" data-bs-toggle="modal" data-bs-target="#cvdownloadmodal" > <i className="bi bi-eye me-1" /> Preview and Download </button> </div> </FormContainer>
之后就可以用formErrors标记对应区域,比如给未填写字段所在区域添加红色边框:
// 示例:假设某个区域对应字段`workExperience` <div className={`section-container ${formErrors.workExperience ? 'error-highlight' : ''}`}> {/* 该区域的表单内容 */} </div>
添加CSS样式:
.error-highlight { border: 1px solid #dc3545; padding: 1rem; border-radius: 0.375rem; }
2. 子组件FormContainer修改
新增接收onErrorsChange回调的props,并监听Formik的错误变化:
import { Form, Formik, useEffect } from "formik"; import React from "react"; import GenerateFields from "./GenerateFields"; import { getFormInitialValue, getFormValidations } from "./utility/formUtils"; import * as Yup from "yup"; import { toast } from "react-toastify"; const FormContainer = ({ initialFieldValues = [], formData = [], onFormSubmit = "", formValidation = [], extraInputClass = "", children, onErrorsChange, {/* 新增接收回调 */} ...otherProps }) => { const withValidation = formValidation ? true : false; const initialValues = Array.isArray(initialFieldValues) ? getFormInitialValue(initialFieldValues) : initialFieldValues; const validationSchema = Yup.object().shape( getFormValidations(formValidation) ); return ( <Formik initialValues={initialValues} validationSchema={withValidation ? validationSchema : ""} onSubmit={ onFormSubmit ? (val, { errors }) => { // 提交时传递错误 if (onErrorsChange) onErrorsChange(errors); onFormSubmit(val, { errors }); } : (val, { errors }) => { console.log("🚀 ~ file: FormContainer.jsx:38 ~ val", errors); if (onErrorsChange) onErrorsChange(errors); // 原有setFormValues逻辑 } } enableReinitialize={true} > {(formAttributes) => { // 实时监听错误变化并传递给父组件 useEffect(() => { if (onErrorsChange) { onErrorsChange(formAttributes.errors); } }, [formAttributes.errors, onErrorsChange]); return ( <Form className="formikContainer row m-2"> {formData.map((fieldAttribute, index) => { return ( <GenerateFields key={index} inputAttributes={fieldAttribute} formActions={formAttributes} withValidation={withValidation} /> ); })} {children && children} </Form> ); }} </Formik> ); }; export default FormContainer;
方法二:通过Ref暴露错误获取方法
如果只需要在特定时机(比如点击预览按钮时)获取错误,可使用useImperativeHandle暴露获取错误的方法。
1. 父组件修改
创建Ref并绑定到FormContainer,在需要时调用获取错误的方法:
import { useRef, useState } from 'react'; const formContainerRef = useRef(null); const [formErrors, setFormErrors] = useState({}); // 比如点击预览按钮时获取错误 const handlePreview = () => { const errors = formContainerRef.current?.getErrors(); setFormErrors(errors); // 后续处理预览逻辑 }; // 修改预览按钮的点击事件 <button type="button" className="btn btn-danger ms-2 mb-2" data-bs-toggle="modal" data-bs-target="#cvdownloadmodal" onClick={handlePreview} > <i className="bi bi-eye me-1" /> Preview and Download </button> // 绑定Ref到FormContainer <FormContainer ref={formContainerRef} initialFieldValues={id != undefined ? cvRecord : cvFormFields} formData={getFieldsByCategory(cvFormFields, selectOption.type)} formValidation={cvFormFields} onFormSubmit={onFormSubmit} > {/* 按钮组 */} </FormContainer>
2. 子组件FormContainer修改
使用forwardRef和useImperativeHandle暴露获取错误的方法:
import { Form, Formik, useFormikContext, useImperativeHandle, forwardRef } from "formik"; import React from "react"; import GenerateFields from "./GenerateFields"; import { getFormInitialValue, getFormValidations } from "./utility/formUtils"; import * as Yup from "yup"; import { toast } from "react-toastify"; // 用forwardRef包裹组件 const FormContainer = forwardRef(({ initialFieldValues = [], formData = [], onFormSubmit = "", formValidation = [], extraInputClass = "", children, ...otherProps }, ref) => { const withValidation = formValidation ? true : false; const initialValues = Array.isArray(initialFieldValues) ? getFormInitialValue(initialFieldValues) : initialFieldValues; const validationSchema = Yup.object().shape( getFormValidations(formValidation) ); // 获取Formik上下文 const formikCtx = useFormikContext(); // 暴露getErrors方法给父组件 useImperativeHandle(ref, () => ({ getErrors: () => formikCtx.errors })); return ( <Formik initialValues={initialValues} validationSchema={withValidation ? validationSchema : ""} onSubmit={onFormSubmit ? onFormSubmit : (val, { errors }) => { console.log("🚀 ~ file: FormContainer.jsx:38 ~ val", errors); // 原有setFormValues逻辑 }} enableReinitialize={true} > {(formAttributes) => ( <Form className="formikContainer row m-2"> {formData.map((fieldAttribute, index) => { return ( <GenerateFields key={index} inputAttributes={fieldAttribute} formActions={formAttributes} withValidation={withValidation} /> ); })} {children && children} </Form> )} </Formik> ); }); export default FormContainer;
内容的提问来源于stack exchange,提问作者Raja Ahsan
相关产品推荐
相关产品推荐

