如何使用useState实现无限滚动分页时向现有对象列表追加新数据
无限滚动分页状态追加的实现修正
问题本质
执行setItems(resources)的写法,本质是用新拉取的数组整体替换了原有的items状态,这是React useState更新的默认逻辑——只要给状态更新函数传入一个确定的值,它就会直接替换掉之前存储的状态,不会自动执行数组合并。
滚动后触发页面刷新的异常,通常来自两个常见问题:
- 没有通过
isFetching做请求节流拦截,短时间内多次触发状态更新导致渲染异常 - 滚动加载的触发源如果是锚点、提交按钮类标签,没有阻止默认行为时会触发页面跳转/提交,表现和页面刷新一致
修正实现
要实现新旧数据的追加合并,需要使用useState的 函数式更新 能力:给setItems传入一个回调函数,回调的入参是当前内存中最新的旧状态值,把旧数组和新拉取的数组合并后返回,作为新的状态值。
同时需要补齐几个关键的逻辑修正:
- 在函数入口处加
isFetching判断,上一批数据还在加载时直接中断执行,避免重复发请求 - 把
isFetching的重置逻辑放到请求的最终回调里,不要和定时器逻辑硬绑定,避免请求还没完成就提前重置状态触发重复请求 - 对初始值为
undefined的items状态做空值兜底,避免出现读取未定义属性的报错
修正后的代码参考:
function fetchMoreListItems() { // 加载中直接拦截,杜绝重复请求 if (isFetching) return; setIsFetching(true); setTimeout(() => { if (!props.resource) { setIsFetching(false); return; } props.resource .follow('activities-collection').follow('next').followAll('item') .then(resources => { // 函数式更新:获取最新旧状态后做数组拼接 setItems(prevItems => { // 兜底初始空状态,直接返回首批拉取结果 return prevItems ? [...prevItems, ...resources] : resources; }); }) .catch(err => console.error('Error fetching resources: ' + err)) // 请求不管成功失败,最终都重置加载状态 .finally(() => setIsFetching(false)); }, 1000); }
常见避坑点
- 不要直接引用外部作用域的
items变量做拼接操作,受闭包特性影响,很可能拿到陈旧的状态值,导致新数据被意外覆盖;使用函数式更新可以保证拿到的永远是最新的状态值 - 检查滚动触发源的DOM属性,如果是带
href的锚点元素或者type="submit"的按钮,绑定事件时需要加preventDefault()阻止默认跳转行为,这是很多场景下出现“自动页面刷新”表现的核心原因 - 当拉取到最后一页、不存在
next关联关系时,记得主动移除滚动监听,避免发起无意义的空请求
内容的提问来源于stack exchange,提问作者Ibrahim Abubaker
相关产品推荐
相关产品推荐

