React中useEffect因refresh函数重复创建引发无限循环的解决咨询
解决useEffect无限循环的方案
方案一:用useRef缓存refresh函数
既然不想在useRefresh里加useCallback,可以在组件内部用useRef把refresh函数存起来,每次组件渲染时更新ref的current值,但不会触发useEffect的依赖变化,彻底切断循环触发的源头。具体代码修改如下:
import { useRef, useEffect } from 'react'; // ... 其他组件代码 const refresh = useRefresh(); const refreshRef = useRef(refresh); // 每次渲染更新refreshRef,确保拿到最新的refresh函数 useEffect(() => { refreshRef.current = refresh; }, [refresh]); useEffect(() => { const handleRefresh = async () => { if (!tokenExpired || !isOpen) return; // 调用缓存的refresh函数 const token = await refreshRef.current(); tokenRef.current = token; }; const fetchStories = async () => { if (isOpen === false) return; try { const result = await axiosPrivate.get(`${BASE_URL}/api/users/stories/${userId}`, { headers: { "x-auth-token": tokenRef.current }, }); dispatch(setUserStories({ userId, stories: result?.data?.stories })); } catch (err) { console.log(err); return; } }; const handleMount = async () => { await handleRefresh(); await fetchStories(); setIsLoading(false); }; handleMount(); }, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]); // 不再将refresh加入依赖数组
方案二:合理使用ESLint忽略注释(谨慎使用)
如果你能确定refresh函数的内部逻辑不会随组件渲染发生变化(比如它没有依赖外部可变状态),可以直接在useEffect的依赖数组后添加ESLint忽略注释,跳过对refresh的依赖检查:
useEffect(() => { // ... 内部逻辑保持不变 handleMount(); }, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]); // eslint-disable-next-line react-hooks/exhaustive-deps
注意:如果后续useRefresh内部的refresh函数依赖了可变值,这个方法会导致调用到旧的refresh函数,引发潜在bug,仅适合逻辑稳定的场景。
方案三:将异步逻辑封装到自定义hook
把挂载时的刷新+请求逻辑抽离成独立的自定义hook,内部集中处理refresh的缓存问题,让组件代码更简洁:
import { useState, useRef, useEffect } from 'react'; function useFetchUserStories(isOpen, userId, tokenExpired, dispatch, axiosPrivate) { const [isLoading, setIsLoading] = useState(true); const refresh = useRefresh(); const refreshRef = useRef(refresh); const tokenRef = useRef(); useEffect(() => { refreshRef.current = refresh; }, [refresh]); useEffect(() => { const handleRefresh = async () => { if (!tokenExpired || !isOpen) return; const token = await refreshRef.current(); tokenRef.current = token; }; const fetchStories = async () => { if (isOpen === false) return; try { const result = await axiosPrivate.get(`${BASE_URL}/api/users/stories/${userId}`, { headers: { "x-auth-token": tokenRef.current }, }); dispatch(setUserStories({ userId, stories: result?.data?.stories })); } catch (err) { console.log(err); return; } }; const handleMount = async () => { await handleRefresh(); await fetchStories(); setIsLoading(false); }; handleMount(); }, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]); return { isLoading }; } // 在组件中直接使用 const { isLoading } = useFetchUserStories(isOpen, userId, tokenExpired, dispatch, axiosPrivate);
内容的提问来源于stack exchange,提问作者Eesa Munir
相关产品推荐
相关产品推荐

