如何解决scroll事件监听中函数缓存state导致动态参数失效问题?
解决无限滚动中状态闭包缓存问题
你遇到的是React闭包陷阱——useEffect的依赖数组为空,组件挂载时创建的triggerRefetch和fetchData函数会一直捕获初始的pointer状态(空字符串),哪怕后续用setPointer更新了状态,这些函数里的pointer始终是旧值,导致无法正确触发下一次请求。
以下是三种可行的解决办法:
方案一:更新useEffect依赖+用useCallback包裹函数
把相关状态和函数加入useEffect的依赖数组,同时用useCallback包裹fetchData和triggerRefetch,确保依赖变化时函数重新生成,从而捕获最新状态:
const fetchData = useCallback((fileType) => { if (pointer === null) return; API.call(pointer).then((res) => setPointer(res)); }, [pointer]); const triggerRefetch = useCallback(() => { if (!hasMore) return; const element = loadingRef?.current?.getBoundingClientRect(); if (element?.top < window.innerHeight && element.bottom >= 0) { fetchData(fileType); } }, [hasMore, fetchData, fileType]); useEffect(() => { triggerRefetch(); const el = document.getElementById("room-detail-container"); el?.addEventListener("scroll", triggerRefetch); return () => el?.removeEventListener("scroll", triggerRefetch); }, [triggerRefetch]);
方案二:用useRef保存最新状态
通过useRef实时同步pointer的最新值,函数里直接访问ref的current属性就能拿到最新状态,避开闭包陷阱:
const [pointer, setPointer] = useState(""); const pointerRef = useRef(pointer); // 同步状态到ref useEffect(() => { pointerRef.current = pointer; }, [pointer]); const fetchData = (fileType) => { if (pointerRef.current === null) return; API.call(pointerRef.current).then((res) => setPointer(res)); }; const triggerRefetch = () => { if (!hasMore) return; const element = loadingRef?.current?.getBoundingClientRect(); if (element?.top < window.innerHeight && element.bottom >= 0) { fetchData(fileType); } }; useEffect(() => { triggerRefetch(); const el = document.getElementById("room-detail-container"); el?.addEventListener("scroll", triggerRefetch); return () => el?.removeEventListener("scroll", triggerRefetch); }, [hasMore, fileType]);
方案三:函数式更新状态(辅助方案)
如果API返回结果需要依赖前一次的pointer状态,可以用函数式更新获取最新值,配合上面的方案使用效果更佳:
API.call(pointer).then((res) => { setPointer(prevPointer => { // 这里能拿到最新的prevPointer return res; }); });
内容的提问来源于stack exchange,提问作者Owenn
相关产品推荐
相关产品推荐

