React-Formik中如何通过API返回值设置Field字段值?
解决Formik自定义组件中API赋值后Yup校验失败的问题
问题场景
使用React-Formik构建表单时,自定义了InvoiceNumberV2组件,需要在useEffect中调用API获取发票编号后,设置Formik的InvoiceNumber字段值。尝试直接给input标签赋值时,界面显示正常,但Yup校验提示值不存在,需正确实现方案。
调用页面代码
return ( <Formik initialValues={{ ClientId: '', CompanyId: '', InvoiceNumber: '', InvoiceDate: Date.parse('1985-10-20'), PaymentStatusId: '', PaymentTerms: '', Total: '', Notes: '', InvoiceDetails: [{ Id: '', Description: '', Quantity: '', Amount: '', VatPercentage: '', VatAmount: '', Total: '' }] }} validationSchema={Yup.object().shape({ InvoiceNumber: Yup.string().required(), InvoiceDate: Yup.string().required(), })} onSubmit={(values) => { alert(JSON.stringify(values)); }} > {({ errors, touched, props }) => ( <Container> <Form> <Field name="InvoiceNumber" component={InvoiceNumberV2} /> {/* 原代码中多余的value={InvoiceNumber}需移除 */} );
自定义组件原代码
import React, { useState } from 'react'; import Form from 'react-bootstrap/Form' import Col from 'react-bootstrap/Col' import Api from './Api'; import { useAuth0 } from '@auth0/auth0-react'; import { useParams } from 'react-router-dom'; const InvoiceNumberV2 = ({ field, // { name, value, onChange, onBlur } form: { touched, errors, setFieldValue }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc. ...props }) => { const [invoiceNumber, setInvoiceNumber] = useState(""); const apiurl = process.env.REACT_APP_API_URL; const { invoicenumber } = useParams(); React.useEffect(()=>{ async function a() { try { let invTemp = invoicenumber; if (!invTemp) { const uri = `${apiurl}/invoice/getNextInvoiceNumber`; let result = await Api.GetApiData(uri, true); invTemp = result.data.InvoiceNumber // 原疑问:如何用invTemp设置字段值? } } catch (error) { } } a(); },[]) return ( <> <Form.Label column="lg" lg={2}> Inv. number </Form.Label> <Col md={4}> <input type="text" column="lg" lg={2} {...field} {...props} /> {touched[field.name] && errors[field.name] && <div className="text-danger">{errors[field.name]}</div>} </Col> </> ); } export default InvoiceNumberV2;
问题原因
直接给input的value属性赋值仅更新了DOM显示,但未同步更新Formik内部维护的表单状态。Yup校验时读取的是Formik内部的初始空值,因此触发必填校验错误。
解决方案
使用Formik提供的setFieldValue方法更新字段值,该方法会同步修改Formik的内部状态,确保校验逻辑能获取到最新值。同时无需额外维护本地state,Formik会自动管理字段状态。
修改后的自定义组件代码
import React from 'react'; import Form from 'react-bootstrap/Form' import Col from 'react-bootstrap/Col' import Api from './Api'; import { useAuth0 } from '@auth0/auth0-react'; import { useParams } from 'react-router-dom'; const InvoiceNumberV2 = ({ field, // { name, value, onChange, onBlur } form: { touched, errors, setFieldValue }, ...props }) => { const apiurl = process.env.REACT_APP_API_URL; const { invoicenumber } = useParams(); React.useEffect(()=>{ async function fetchInvoiceNumber() { try { let invTemp = invoicenumber; if (!invTemp) { const uri = `${apiurl}/invoice/getNextInvoiceNumber`; let result = await Api.GetApiData(uri, true); invTemp = result.data.InvoiceNumber; } // 使用setFieldValue更新Formik内部状态 setFieldValue(field.name, invTemp); } catch (error) { console.error('获取发票编号失败:', error); } } fetchInvoiceNumber(); }, [field.name, setFieldValue, invoicenumber, apiurl]) // 添加完整依赖项,避免useEffect的无限循环或过时闭包 return ( <> <Form.Label column="lg" lg={2}> Inv. number </Form.Label> <Col md={4}> <input type="text" column="lg" lg={2} {...field} {...props} /> {touched[field.name] && errors[field.name] && <div className="text-danger">{errors[field.name]}</div>} </Col> </> ); } export default InvoiceNumberV2;
关键修改点
- 移除本地state
invoiceNumber,直接依赖Formik的状态管理 - 在useEffect中获取到发票编号后,调用
setFieldValue(field.name, invTemp)同步更新Formik状态 - 给useEffect添加完整依赖项,符合React Hooks规范
- 调用页面中移除
<Field>标签上多余的value={InvoiceNumber}属性
内容的提问来源于stack exchange,提问作者user1131926
相关产品推荐
相关产品推荐

