一行代码顶二十行代码! 🔧 修复 React 聊天室滚动加载问题 Bugfix 解决方法分享

前言

之前分享的 React Web IM 聊天室 相关文章链接 有一个滚动加载的 bug,就是向上滚动请求下一页的分页接口。会频繁触发直到拉取完所有数据。

问题排查

首先,看看 Dom 结构。为了实现用户向上滚动等同于向下滚动,我使用了 flex-col-reverse。顶部有一个 加载更多的参考点 ref={loadMoreRef},作为触发下一页请求的标记。每当这根"线"进入视口时,就会触发加载数据。

tsx 复制代码
  <div
    className="no-scrollbar flex flex-1 flex-col-reverse overflow-y-auto p-4"
    style={{ overflowAnchor: 'none' }}
  >
    {/* 用于自动滚动到底部的参考元素 */}
    <div ref={messagesEndRef} />

    {/* 消息列表 */}
    <div className="flex flex-1 flex-col-reverse gap-4">
      {/* 空白占位元素,用于消息少时将消息撑到底部 */}
      <div className="block flex-shrink flex-grow" />

      {/* 聊天记录对话的 DOM 渲染部分 */}
    </div>

    {/* 添加没有更多消息的提示 */}
    {!hasNextPage && messages.length > 0 && (
      <div className="flex justify-center py-2 text-sm text-gray-400">没有更多消息了</div>
    )}

    {/* 加载状态与加载更多参考点 */}
    {isFetchingNextPage ? (
      <div className="flex justify-center py-2">
        <Spin size="small" />
      </div>
    ) : (
      <div ref={loadMoreRef} className="h-1 w-full" />
    )}

    {/* 无消息提示 */}
    {!isLoading && messages.length === 0 && (
      <div className="flex h-full items-center justify-center text-gray-400">暂无聊天记录</div>
    )}
  </div>

问题:每次触发加载请求时,界面会显示 Loading 状态。当请求响应后,加载更多的参考点又会出现在视口中,触发下一页请求。这种循环导致了频繁的接口请求,给用户带来"抖动"的不佳体验。

如何解决

方式一:防抖

我曾尝试通过防抖来延迟请求响应的时间,但这个方式没有完全解决问题。虽然延迟响应能够减少请求频率,但由于 loaderMoreRef 仍然可见,下一次请求仍会被触发,导致问题无法根本解决。

方式二:记录滚动位置

我考虑记录当前滚动到顶部的最后一条消息位置,然后将新数据插入到前面。这种方法能使容器被撑开,loaderMoreRef 被挤上去,直到用户主动滚动到它的位置才会触发下一次请求。经过一番摸索,虽然效果达到了,但代码逻辑变得复杂了许多。

方式三:CSS 技巧(推荐)

我想应该不只有我会遇到这个问题。别人也应该会遇到啊。遂在网上搜了下。react聊天室,列表加载数据滚动条还在顶部?

最后一行代码解决 style={{ overflowAnchor: 'none' }} 一个冷门的 css 属性解决了我的问题。在滚动容器上添加这个属性。这样子就解决了新增数据的时候滚动容器还在最顶部进而导致多次请求的问题了。

MDN overflow-anchor 介绍 作用就是插入新数据时候保留原先的滚动锚点。简单理解就是让浏览器别帮我管理滚动条默认行为。

tsx 复制代码
  <div
    className="no-scrollbar flex flex-1 flex-col-reverse overflow-y-auto p-4"
    style={{ overflowAnchor: 'none' }}
  >

这个方法不仅解决了聊天室中的问题,还有一个相关案例:当图片加载较慢时,尤其是漫画网站,垂直布局的图片会导致页面跳动,重新定位滚动条位置,而 overflow-anchor 同样能解决这个问题。www.zhangxinxu.com/wordpress/2...

结语

所以希望以后做类似功能的小伙伴可以知道这个属性 overflowAnchor: 'none'

相关推荐
kyriewen20 分钟前
我排查了一个React内存泄漏——罪魁祸首是这3个被忽略的清理函数
前端·javascript·面试
IT_陈寒27 分钟前
我又被JavaScript的隐式类型转换坑了
前端·人工智能·后端
其美杰布-富贵-李1 小时前
03 ref、reactive 与 computed 响应式数据
前端·javascript·vue.js
OpenTiny社区1 小时前
GenUI SDK v1.3.0 发布|多框架兼容,一键换物料,渲染器 & 演练场全面增强!
前端·ai编程
用户938515635071 小时前
useRef + Web Worker 实战:React 如何优雅地拥抱多线程
前端·javascript·react.js
他们叫我秃子1 小时前
前端开发转 Go 全栈(五):终于遇到熟人了,Go 的闭包和高阶函数原来这么像 JavaScript
前端·后端·go
用户61595868000221 小时前
从 0 写一个迷你 wujie:用原生 Web API 实现微前端
前端
妙码生花1 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十七):AI时代对组件封装的新理解、agInput 统一入口,动态表单预备
前端·后端·go
fairyly1 小时前
接手新项目不用硬啃源码,我的 TRAE Work 快速上手实战工作流
前端·ai编程·trae
搬砖记录员1 小时前
用 PyAutoGUI 手搓录屏"老板键":从原理到四套可跑源码,附工程化落地思考
前端