如何在useEffect中使用Formik的setFieldValue避免无限循环
修复Formik与useEffect的无限循环问题
核心原因
每次组件重新渲染时,useFormik返回的formik对象都是全新的引用。如果将整个formik对象加入useEffect依赖项,会导致useEffect在每次渲染时都触发,进而调用setFieldValue引发组件重新渲染,形成无限循环。
解决方案
方案1:仅依赖formik.setFieldValue而非整个formik对象
useFormik返回的setFieldValue是稳定引用,不会随组件渲染而变化。修改依赖项,只传入这个方法即可避免循环:
import { useGetMemberFunctionalityWithLimitMutation, } from '../../../../redux/services/employeeManagement/employeeManagementApi'; import { useFormik } from 'formik'; import { useState, useEffect } from 'react'; function A1_EmployeeList() { const [getMemberFunctionalityWithLimit] = useGetMemberFunctionalityWithLimitMutation(); const [paginationObj, setPaginationObj] = useState({ page: 1, pageSize: 10, employeeListQuery: { employerUuid, fullnameOrId: '', payrollGroupUuid: '', schemeUuid: '', }, }); const searchFormSubmit = (values) => { const { fullnameOrId, payrollGroupUuid, schemeUuid } = values; const employeeListQuery = { ...paginationObj.employeeListQuery, fullnameOrId: fullnameOrId || '', payrollGroupUuid: payrollGroupUuid || '', schemeUuid: schemeUuid || '', }; setPaginationObj({ ...paginationObj, page: 1, employeeListQuery, }); }; const formik = useFormik({ initialValues: { fullnameOrId: '', schemeUuid: '', payrollGroupUuid: '', dataList: [], }, onSubmit: (values) => { searchFormSubmit(values); }, }); // 提取稳定的setFieldValue方法 const { setFieldValue } = formik; useEffect(() => { const { employeeListQuery, pageSize, page } = paginationObj; getMemberFunctionalityWithLimit({ page, limit: pageSize, employeeListQuery, }) .unwrap() .then(({ payload }) => { setFieldValue('dataList', payload.employeeList); }) .catch((error) => { console.log(error); }); }, [setFieldValue, getMemberFunctionalityWithLimit, paginationObj]); return ( // 组件JSX内容 ); }
方案2:使用useFormikContext钩子(适合嵌套组件场景)
如果你的useEffect写在Formik表单的嵌套组件中,可以用useFormikContext获取setFieldValue,同样只依赖该方法:
import { useFormikContext } from 'formik'; // 组件内部 const { setFieldValue } = useFormikContext(); useEffect(() => { const { employeeListQuery, pageSize, page } = paginationObj; getMemberFunctionalityWithLimit({ page, limit: pageSize, employeeListQuery, }) .unwrap() .then(({ payload }) => { setFieldValue('dataList', payload.employeeList); }) .catch((error) => { console.log(error); }); }, [setFieldValue, getMemberFunctionalityWithLimit, paginationObj]);
方案3:用useCallback包裹API回调(可选)
如果需要进一步保证回调逻辑的稳定性,可以用useCallback包裹API调用逻辑:
import { useState, useEffect, useCallback } from 'react'; // 组件内部 const fetchEmployeeData = useCallback(async () => { const { employeeListQuery, pageSize, page } = paginationObj; try { const { payload } = await getMemberFunctionalityWithLimit({ page, limit: pageSize, employeeListQuery, }).unwrap(); formik.setFieldValue('dataList', payload.employeeList); } catch (error) { console.log(error); } }, [formik.setFieldValue, getMemberFunctionalityWithLimit, paginationObj]); useEffect(() => { fetchEmployeeData(); }, [fetchEmployeeData]);
额外提示
原代码中searchFormSubmit里的payrollGroupUuid赋值逻辑疑似笔误(用schemeUuid作为判断条件),建议检查是否应为payrollGroupUuid: payrollGroupUuid || ''。
内容的提问来源于stack exchange,提问作者blizzard
相关产品推荐
相关产品推荐

