状态变化时如何触发useEffect重新执行?附相关代码求纠错
核心问题原因
- 切换filter时没有清空旧的
img列表数据,也没有重置page页码,新数据是追加到旧数据后面,所以会先显示旧内容再显示新内容 - 初始
img状态定义为字符串,后续当成数组操作,存在类型错误 - 存在多余冗余逻辑:空逻辑的useEffect、完全不需要的forceUpdate
- 滚动事件没有在组件卸载时解绑,会造成内存泄漏,且每次渲染都会重复绑定事件
- 依赖项不完整:useEffect里用到了
img相关值,但没加到依赖数组里,存在闭包陷阱
修正后完整代码
const PAGE_NUMBER = 1; export const SimpleInfiniteList = () => { // 初始值改为数组,符合后续操作类型 const [img, setImg] = useState([]) const [page, setPage] = useState(PAGE_NUMBER) const [filter, setFilter] = useState('top') // 防止滚动事件重复触发请求的锁 const [loading, setLoading] = useState(false) // 切换filter时重置page和img列表,直接渲染全新数据 useEffect(() => { setPage(PAGE_NUMBER) setImg([]) }, [filter]) // 数据请求逻辑,依赖filter、page变化触发 useEffect(() => { setLoading(true) const after = img.length > 0 ? `t3_${img[img.length - 1].data.id}` : '' fetch(`https://www.reddit.com/r/ProgrammerHumor/${filter}.json?after=${after}&page=${page}&limit=2`) .then(response => response.json()) .then(data => { // page为1时直接替换数据,否则追加(对应分页加载) if(page === PAGE_NUMBER) { setImg(data.data.children) } else { setImg(prev => [...prev, ...data.data.children]) } }) .finally(() => setLoading(false)) }, [filter, page]) const scrollToEnd = () => { // 加载中不重复触发请求 if(!loading) { setPage(prev => prev + 1) } } // 滚动事件绑定放到useEffect里,组件卸载时解绑 useEffect(() => { const handleScroll = () => { const bottom = Math.ceil(window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight if (bottom) { scrollToEnd() console.log('loading....') } } window.onscroll = handleScroll return () => window.onscroll = null }, [loading]) console.log(img) // 此处补充你原有列表渲染逻辑即可 }
关键修改说明
- 切换filter时会先清空旧数据、重置页码为1,请求到新数据后直接替换整个列表,不会再残留旧数据
- 新增loading状态防止滚动时重复触发分页请求
- 滚动事件绑定逻辑优化,避免内存泄漏
- 用setImg的函数式更新写法避免闭包问题,不需要额外的forceUpdate
内容的提问来源于stack exchange,提问作者Sudo
相关产品推荐
相关产品推荐

