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

如何基于状态变量更新Antd Form Item输入值?Google Places场景

问题描述

在Antd地址表单中集成Google Places API实现自动填充,使用react-google-autocomplete包(具体用了usePlacesWidget钩子)可成功获取地址信息并更新foundAddress状态,但选中地址后city等Form字段无法自动填充。尝试过Form.Item的initialValue、getValueFromEvent等属性,Input组件的value设置,Form的onValuesChange方法,以及单独维护city状态,均无效。但使用普通<input>或设置Input的placeholder时能正常显示解析后的地址值,请问Antd表单值处理环节哪里出错了?


代码结构

NewForm.tsx

const NewForm = (props: Props) => {
  const [form] = Form.useForm();
  const formRef = useRef<any>(null);

  const onFinish = async (values: any) => {
    const newAddressData: NewAddressInput = {
      name: values.name?.toUpperCase(),
      company: values.company?.toUpperCase(),
      street1: values.street1.toUpperCase(),
      street2: values.street2?.toUpperCase(),
      city: values.city.toUpperCase(),
      state: values.state.toUpperCase(),
      zip: values.zip.toUpperCase(),
      country: values.country.toUpperCase(),
      phone: values.phone,
      notes: values.notes,
    };
    // 此处省略提交逻辑
  };

  const onFinishFailed = (errorInfo: any) => {
    console.log('Failed:', errorInfo);
    if (errorInfo.errorFields.length > 0) {
      errorInfo.errorFields.map((field: { errors: string[]; }, index: any) => {
        toast.error(field.errors[0] as string);
      });
    }
  };

  useEffect(() => {
    console.log('form values in useEffect', form.getFieldValue('city'));
  }, [form]);

  console.log('form values', form.getFieldValue('city'));

  return (
    <Dashboard>
      <Form
        ref={formRef}
        form={form}
        name="newClientForm"
        labelCol={{ span: 3 }}
        wrapperCol={{ span: 22 }}
        initialValues={{ remember: true }}
        onFinish={onFinish}
        onFinishFailed={onFinishFailed}
        autoComplete="off"
        className={styles.createClientForm}
        layout="horizontal"
        onValuesChange={(changed, all) => console.log('values change', changed, all)}
        onFieldsChange={(first, second) => console.log('fields change', first, second)}
        fields={[
          {
            name: 'city',
            value: form.getFieldValue('city')
          }
        ]}
      >
        <h2>Create a New Client</h2>
        <Divider />
        <CreateAddressFields />
        <Form.Item style={{ textAlign: 'center' }}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </Form.Item>
      </Form>
    </Dashboard>
  );
};

CreateAddressFields.tsx

function CreateAddressFields({ }: Props) {
  const [foundAddress, setFoundAddress] = useState<NewAddressInput | null>(null);
  const [city, setCity] = useState<string | null>(null);

  const inputRef = useRef<any>(null);
  const options = {
    componentRestrictions: { country: ["ca", "us"] },
    fields: ["address_components", "geometry", "icon", "name"],
    types: ["address"]
  };

  const { ref, autocompleteRef } = usePlacesWidget({
    apiKey: PLACES_API_KEY,
    onPlaceSelected: (place, inputRef, something) => {     
      console.log('place selected', place);
      const address = place.address_components;
      let { street1, street2, city, state, zip, country }: NewAddressInput = {
        street1: '',
        street2: '',
        city: '',
        zip: '',
        state: '',
        country: ''
      };
      if (address) {
        console.log('going through found address');
        for (const component of address) {
          const type = component.types[0];
          switch (type) {
            case "street_number":
              street1 = `${component.long_name} ${street1}`;
              break;
            case "route": {
              street1 += component.short_name;
              break;
            }
            case "postal_code": {
              zip = `${component.long_name}${zip}`;
              break;
            }
            case "locality":
              city = `${component.long_name}${city}`;
              break;
            case "administrative_area_level_1": {
              state = `${component.short_name}${state}`;
              break;
            }
            case "country":
              country = `${component.short_name}${country}`;
              break;
            default:
              break;
          }
        }
        setFoundAddress({ street1, street2, city, state, zip, country });
        console.log('results', { street1, street2, city, state, zip, country });
      }
    },
    options
  });

  const handleChange = (event: any) => {
    console.log('name', event.target.name);
    const name = event.target.name;
    console.log('value', event.target.value);
    const value = event.target.value;
    setFoundAddress({ ...foundAddress!, [event.target.name]: event.target.value });
    if (name === 'city') {
      setCity(value);
    }
  };

  return (
    <>
      <Form.Item
        label="Name: "
        name="name"
        rules={[{ required: true, message: 'Please input your name!' }]}>
        <Input />
      </Form.Item>
      <Form.Item
        label="Company: "
        name="company">
        <Input />
      </Form.Item>
      <Form.Item
        label="Street Info 1:"
        name="street1"
        rules={[{ required: true, message: 'Please input your street1!' }]}>
        <Input
          placeholder=''
          ref={(c) => {
            inputRef.current = c;
            if (c) ref.current = c.input;
          }}
        />
      </Form.Item>
      <Form.Item
        label="Street Info 2:"
        name="street2">
        <Input />
      </Form.Item>
      <Form.Item
        label="City:"
        name="city"
        getValueProps={(v) => {
          return ({ name: 'city', value: v });
        }}
        rules={[{ required: true, message: 'Please input your city!' }]}>
        <Input
          onChange={handleChange}
          value={city!}
        />
      </Form.Item>
      <Form.Item
        label="State:"
        name="state"
        rules={[{ required: true, message: 'Please input your state!' }]}>
        <Input />
      </Form.Item>
      <Form.Item
        label="Zip:"
        name="zip"
        rules={[{ required: true, message: 'Please input your zip!' }]}>
        <Input />
      </Form.Item>
      <Form.Item
        label="Country:"
        name="country"
        rules={[{ required: true, message: 'Please choose your country!' }]}>
        <Input />
      </Form.Item>
      <Form.Item
        label="Phone:"
        name="phone"
        rules={[{ required: true, message: 'Please input your phone number!' }]}>
        <Input />
      </Form.Item>
      <Form.Item
        label="Notes:"
        name="notes">
        <Input />
      </Form.Item>
    </>
  );
}

问题原因与解决方案

核心问题

  1. 表单状态脱节:手动维护的foundAddress、city等本地状态,与Antd Form实例内部管理的字段值完全独立,解析后的地址无法同步到Form字段。
  2. 受控组件冲突:给Input手动设置value={city!},覆盖了Antd Form对Input的受控管理逻辑,导致Form无法自动更新Input显示内容。
  3. 错误的fields配置:NewForm中Form组件的fields属性设置了循环依赖内容,干扰Form正常状态更新。

修复步骤

1. 传递Form实例给子组件

修改NewForm中引入CreateAddressFields的代码,传入Form实例:

<CreateAddressFields form={form} />

同时更新CreateAddressFields的Props定义:

import { FormInstance } from 'antd/es/form';

interface Props {
  form: FormInstance;
}

function CreateAddressFields({ form }: Props) {
  // ... 原有代码
}

2. 移除本地状态,直接更新Form字段

在onPlaceSelected中,解析完地址后调用Form实例的setFieldsValue方法,直接同步到表单字段,不再需要本地状态:

const { ref, autocompleteRef } = usePlacesWidget({
  apiKey: PLACES_API_KEY,
  onPlaceSelected: (place) => {     
    const address = place.address_components;
    let street1 = '', street2 = '', city = '', state = '', zip = '', country = '';
    if (address) {
      for (const component of address) {
        const type = component.types[0];
        switch (type) {
          case "street_number":
            street1 = `${component.long_name} ${street1}`;
            break;
          case "route":
            street1 += component.short_name;
            break;
          case "postal_code":
            zip = component.long_name;
            break;
          case "locality":
            city = component.long_name;
            break;
          case "administrative_area_level_1":
            state = component.short_name;
            break;
          case "country":
            country = component.short_name;
            break;
          default:
            break;
        }
      }
      // 直接更新Form字段
      form.setFieldsValue({
        street1,
        city,
        state,
        zip,
        country
      });
    }
  },
  options
});

3. 恢复Form对Input的受控管理

移除CreateAddressFields中Input的手动value设置和自定义handleChange(无额外逻辑时,Form会自动处理字段值变化):

// 修改City对应的Form.Item和Input
<Form.Item
  label="City:"
  name="city"
  rules={[{ required: true, message: 'Please input your city!' }]}>
  <Input />
</Form.Item>

4. 移除错误的fields配置

删除NewForm中Form组件的fields属性,避免干扰Form状态管理:

<Form
  // 移除以下配置
  // fields={[{name: 'city', value: form.getFieldValue('city')}]}
>

验证效果

完成修改后,选中Google Places地址时,解析后的city、state等字段会自动同步到Antd Form对应的Input中,表单提交可正常获取值,同时用户手动修改字段也能正常生效。


内容的提问来源于stack exchange,提问作者m3.b

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:03:32