You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

关键修改点

  1. 移除本地stateinvoiceNumber,直接依赖Formik的状态管理
  2. 在useEffect中获取到发票编号后,调用setFieldValue(field.name, invTemp)同步更新Formik状态
  3. 给useEffect添加完整依赖项,符合React Hooks规范
  4. 调用页面中移除<Field>标签上多余的value={InvoiceNumber}属性

内容的提问来源于stack exchange,提问作者user1131926

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 10:50:03