Formik+Ant Design表单输入随输入重渲染失焦问题求助
解决Formik + Ant Design Form.List输入框重渲染失焦问题
问题根源分析
- 组件重复创建:
FormInput定义在父组件内部,每次Formik状态更新时,父组件重渲染会重新创建FormInput组件,导致输入框被卸载后重新挂载,失去焦点。 - 重复状态更新:
FormInput中同时调用formik.handleChange和formik.setFieldValue,触发两次状态更新,加重重渲染。 - 字段路径不匹配:手动拼接的Formik字段路径与Form.List的内部字段管理逻辑冲突,导致状态更新时触发全量列表重渲染。
修复步骤
1. 缓存FormInput组件
将FormInput用React.memo包裹,或者移到父组件外部,避免每次父组件重渲染都重新创建该组件:
const FormInput = React.memo(({ formik, name, placeholder, ...rest }) => ( <div className="space-y-1 w-full pt-3 pr-2"> <label className="font-bold text-sm text-secondary"> Web Link <span className="text-red-500">*</span> </label> <Input {...formik.getFieldProps(name)} placeholder={placeholder} {...rest} /> </div> ));
2. 移除重复的状态更新逻辑
formik.getFieldProps(name)已经内置了value和onChange逻辑,不需要手动设置value或重复调用状态更新方法,这会触发多余的重渲染。
3. 对齐Form.List与Formik的字段路径
直接使用Form.List提供的name参数拼接Formik字段路径,避免手动拼接导致的路径错误:
<Form.List name="websites"> {(fields, { add, remove }) => { return ( <div key="test"> {fields.map(({ name, key, fieldKey, ...restField }) => { return ( <div key={fieldKey}> <Form.Item {...restField} name={[name, 'domain']} fieldKey={[fieldKey, 'domain']} validateTrigger={["onChange", "onBlur"]} rules={[ { required: true, message: 'Domain is required' } ]} noStyle className="w-full" > <div className="flex items-end w-full"> <FormInput formik={formik} name={`websites[${name}].domain`} placeholder="URL" /> {fields.length > 1 && ( <Button type="default" size="small" icon={<LuTrash2 size={20}/>} onClick={() => { const deletedItem = formik.values.websites[name]; if (deletedItem) { // 函数式更新避免依赖旧状态 formik.setFieldValue('delete_websites', prev => [...prev, deletedItem]); remove(name); } }} className="mb-1 bg-[#F06548] border-[#F06548] text-white flex items-center justify-center h-full" /> )} </div> </Form.Item> </div> ); })} <div className="mt-5 w-full"> <AntButton type="dashed" onClick={() => add({ domain: '' })} icon={<PlusOutlined />} className="w-full border-2 border-dashed border-gray-500" > Add Website </AntButton> </div> </div> ); }} </Form.List>
4. 优化Formik初始化配置
确保websites数组的每个元素都包含domain字段,避免渲染异常:
const formik = useFormik({ enableReinitialize: true, initialValues: { sp_id: data?.id, websites: (data?.websites || []).map(item => ({ domain: item.domain || '' })), create_websites: [], delete_websites: [], update_websites: [] }, validationSchema: Yup.object({ sp_id: Yup.string().required("This field is required and can't be changed"), websites: Yup.array().of(Yup.object({ domain: Yup.string().required('Domain is required') })) }), onSubmit: (values) => { console.log('submit:', values) } });
额外优化建议
- 使用
formik.setFieldValue的函数式更新模式,避免依赖旧状态值,减少不必要的重渲染触发。 - 如果父组件有频繁更新的props,用
useMemo缓存initialValues,避免enableReinitialize触发不必要的表单重新初始化。
内容的提问来源于stack exchange,提问作者user23319726
相关产品推荐
相关产品推荐

