如何正确复用Formik组件?表单回填时输入框响应延迟
问题根源
回填延迟完全是前端Formik使用方式不当导致的,和后端接口无关:
- 连续调用8次
formik.setFieldValue,默认每次调用都会触发全表单重渲染+全字段校验,8次重复状态更新叠加在React Native的TextInput渲染链路上,就会出现数秒卡顿 - Formik默认开启输入实时校验,回填阶段的无意义校验进一步拉长了渲染耗时
- useEffect缺失必要依赖,存在闭包引用旧值的问题,会加剧状态更新异常
可直接落地的优化方案
1. 替换单字段赋值为批量赋值
删除逐个调用setFieldValue的逻辑,使用setValues一次性完成所有字段赋值,将8次状态更新压缩为1次,回填时关闭自动校验避免无意义性能消耗:
// 接口返回数据后替换原有8个setFieldValue逻辑 const data = response.data; formik.setValues({ id: data.id, title: data.attributes.title, name_lastname: data.attributes.name_lastname, address: data.attributes.address, city: data.attributes.city, district: data.attributes.district, phone: data.attributes.phone, reference: data.attributes.reference, }, false); // 第二个参数传false,赋值时不触发校验
2. 调整useFormik配置减少无效渲染
补充性能相关配置,从根源减少不必要的重渲染和校验触发:
const formik = useFormik({ initialValues: initialValues(), validationSchema: Yup.object(validationSchema()), enableReinitialize: true, // 允许初始值动态更新 validateOnChange: false, // 关闭输入实时校验,仅失焦/提交时校验 validateOnBlur: true, onSubmit: async (formData) => { setLoading(true); try { if (newAddress) { const response = await addAddressApi(auth, formData); if (response.error) throw "Error al agregar Dirección"; } else { await updateAddressApi(auth, formData); } navigation.goBack(); } catch (error) { Toast.show(error, { position: Toast.positions.CENTER, }); } setLoading(false); }, });
3. 修复useEffect闭包问题
补全useEffect依赖数组,避免闭包引用旧状态导致的更新异常,同时增加接口返回空值的兜底判断:
useEffect(() => { (async () => { if (route.params?.idAddress) { setNewAddress(false); navigation.setOptions({ title: "Actualizar Dirección" }); const response = await getExactAddressesApi( auth, route.params.idAddress ); if (!response?.data) return; const data = response.data; formik.setValues({ id: data.id, title: data.attributes.title, name_lastname: data.attributes.name_lastname, address: data.attributes.address, city: data.attributes.city, district: data.attributes.district, phone: data.attributes.phone, reference: data.attributes.reference, }, false); } })(); }, [route.params, formik, auth]);
进阶优化(可选)
完成上述修改后回填耗时会降到100ms以内,完全感知不到延迟。如果后续表单字段继续增多,可以配合React.memo包装单个TextInput组件,或者用Formik的useField钩子拆分独立字段组件,进一步避免单字段输入时全表单重渲染。
内容的提问来源于stack exchange,提问作者user8282183292
相关产品推荐
相关产品推荐

