如何基于状态变量更新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> </> ); }
核心问题
- 表单状态脱节:手动维护的
foundAddress、city等本地状态,与Antd Form实例内部管理的字段值完全独立,解析后的地址无法同步到Form字段。 - 受控组件冲突:给Input手动设置
value={city!},覆盖了Antd Form对Input的受控管理逻辑,导致Form无法自动更新Input显示内容。 - 错误的
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

