将redux thunk传入自定义hook时useEffect依赖缺失问题如何解决
问题根因
你遇到的重复请求问题核心是:在SomeListing组件每次渲染时,都会执行fetchPropertyByUserId({userId: userId, page: page})生成一个全新的thunk实例,哪怕传入的参数没有变化,生成的对象引用也会更新。如果直接将dispatchAction加入useEffect依赖数组,每次渲染都会触发effect执行,自然会反复发起请求。
方案1:调整自定义Hook传参结构(更推荐)
调整useFetchData的入参规则,不再直接传入生成好的thunk实例,而是传入thunk创建函数和请求参数,从根源保证依赖的稳定性。
修改后的自定义Hook代码
const useFetchData = (createDispatchAction, params) => { const dispatch = useDispatch(); const [loadMoreLoading, setLoadMoreLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(); useEffect(() => { const fetchData = async () => { setLoadMoreLoading(true); // 内部生成thunk实例 const dispatchAction = createDispatchAction(params); const resultAction = await dispatch(dispatchAction); if (resultAction.meta.requestStatus === 'rejected') { setErrorMsg(resultAction.payload.message); } setLoadMoreLoading(false); }; fetchData(); }, [dispatch, createDispatchAction, params]); return [loadMoreLoading, errorMsg]; };
修改后的组件调用代码
const SomeListing = ({userId}) => { const [page, setPage] = useState(1); // 用useMemo稳定参数引用,只有userId、page变化时才生成新的参数对象 const fetchParams = useMemo(() => ({ userId, page }), [userId, page]); const [loadMoreLoading, errorMsg] = useFetchData( fetchPropertyByUserId, // 直接传入顶层定义的thunk创建函数,引用永久稳定 fetchParams ); };
这个方案完全符合React的依赖声明规范,不需要额外跳过eslint检查,只有当userId或page变化时才会重新发起请求,完全匹配需求。
方案2:用useRef缓存thunk实例(改造成本更低)
如果不想调整现有传参逻辑,可以用useRef缓存最新的dispatchAction,避开对动态生成的thunk实例的依赖声明。
修改后的自定义Hook代码
const useFetchData = (dispatchAction, page) => { const dispatch = useDispatch(); const [loadMoreLoading, setLoadMoreLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(); // 用ref存储最新的dispatchAction实例 const latestDispatchAction = useRef(dispatchAction); // 单独维护ref更新,dispatchAction变化时自动同步到ref useEffect(() => { latestDispatchAction.current = dispatchAction; }, [dispatchAction]); useEffect(() => { const fetchData = async () => { setLoadMoreLoading(true); // 从ref中读取最新的实例执行 const resultAction = await dispatch(latestDispatchAction.current); if (resultAction.meta.requestStatus === 'rejected') { setErrorMsg(resultAction.payload.message); } setLoadMoreLoading(false); }; fetchData(); }, [dispatch, page]); return [loadMoreLoading, errorMsg]; };
这个方案不需要修改上层组件的调用代码,也不会触发eslint的依赖警告,同样可以实现只有page变化时才发起请求的逻辑。
内容的提问来源于stack exchange,提问作者Por
相关产品推荐
相关产品推荐

