React中useEffect触发无限循环问题排查求助
无限循环原因分析及解决方法
核心原因
1. data引用持续变化
在useGetTaskAccordingToStatus中,data是通过userQueries?.map(...)生成的数组,每次组件渲染都会创建新的数组引用——哪怕数组里的内容完全没变。第二个useEffect把data作为依赖项,所以每次组件渲染都会触发这个useEffect,调用sFinalState更新finalState状态,进而导致组件再次渲染,重新生成新的data,形成无限循环。
2. columnDataWithTaskProperty的不必要更新
第一个useEffect依赖columnData,每次columnData变化时,都会通过map生成新数组并更新columnDataWithTaskProperty状态。由于数组是引用类型,map每次都会返回新数组,导致columnDataWithTaskProperty的引用持续变化,进一步触发第二个useEffect执行,加剧循环频率。
解决方法
优化useGetTaskAccordingToStatus,稳定data引用
用useMemo缓存data,只有当userQueries中的查询结果真正变化时,才重新生成数组:
const useGetTaskAccordingToStatus = () => { const { active_project } = useSelector(projectDataInStore); const userQueries = useQueries({ queries: statesOfTaskManager.map((status) => { return { queryKey: [status, active_project], queryFn: () => customAxiosRequestForGet("/task", { status, projectName: active_project, }), }; }), }); // 用useMemo缓存data,避免每次渲染生成新引用 const data = useMemo(() => { return userQueries?.map((item) => item?.data?.data); }, [userQueries]); return { data }; };
注:onSuccess回调无需返回数据,React Query会自动处理查询结果,可直接移除。
用衍生状态替代状态更新,彻底消除循环
把columnDataWithTaskProperty和finalState改成useMemo计算的衍生状态,不需要单独用useState存储,避免不必要的状态更新:
const useBoard = () => { const { data: columnData } = useGetColumnQuery(); const { data } = useGetTaskAccordingToStatus(); // 用useMemo直接计算columnDataWithTaskProperty const columnDataWithTaskProperty = useMemo(() => { return columnData?.data?.map((item) => ({ name: item.name, _id: item._id, tasks: [], })) || []; }, [columnData]); // 用useMemo直接计算finalState,仅在依赖项变化时重新计算 const finalState = useMemo(() => { if (!columnDataWithTaskProperty.length || !data?.length) return []; return columnDataWithTaskProperty.map((column) => ({ ...column, tasks: data.flat().filter((task) => task.status === column.name), })); }, [columnDataWithTaskProperty, data]); return { finalState, }; };
额外检查点
如果statesOfTaskManager是动态生成的数组,也需要用useMemo缓存它,否则会导致useQueries频繁触发重复查询,间接引发循环。
内容的提问来源于stack exchange,提问作者nidhi sharma
相关产品推荐
相关产品推荐

