如何在React+Redux中实现滚动加载分页优化700+记录性能
实现React+Redux下的滚动懒加载优化性能
我之前也碰到过一次性加载几百条数据导致页面卡顿、加载耗时久的问题,用滚动懒加载完全解决了这个痛点。结合你用的React+Redux技术栈,给你梳理一套完整的落地方案:
第一步:Redux状态层设计
首先要在Redux的状态里维护分页相关的核心参数,比如当前页码、每页条数、加载状态、是否还有更多数据,以及已经加载的列表数据。
示例Reducer
// reducers/listReducer.js const initialState = { data: [], currentPage: 1, pageSize: 20, isLoading: false, hasMore: true, // 标记是否还有剩余数据可加载 }; const listReducer = (state = initialState, action) => { switch (action.type) { case 'LIST_LOAD_START': return { ...state, isLoading: true }; case 'LIST_LOAD_SUCCESS': // 合并新数据到已有列表 const newData = [...state.data, ...action.payload]; // 判断是否还有更多:如果返回的新数据量小于每页条数,说明没有剩余了 const hasMore = action.payload.length === state.pageSize; return { ...state, data: newData, currentPage: state.currentPage + 1, isLoading: false, hasMore, }; case 'LIST_LOAD_FAILURE': return { ...state, isLoading: false }; default: return state; } }; export default listReducer;
对应的Action Creators
// actions/listActions.js export const loadListStart = () => ({ type: 'LIST_LOAD_START' }); export const loadListSuccess = (data) => ({ type: 'LIST_LOAD_SUCCESS', payload: data }); export const loadListFailure = () => ({ type: 'LIST_LOAD_FAILURE' }); // 异步请求的Thunk(如果用redux-thunk中间件) export const fetchList = (page, pageSize) => async (dispatch) => { dispatch(loadListStart()); try { // 替换成你的实际API请求,传入分页参数 const response = await fetch(`/api/your-list?page=${page}&pageSize=${pageSize}`); const data = await response.json(); dispatch(loadListSuccess(data)); } catch (error) { dispatch(loadListFailure()); console.error('列表加载失败:', error); } };
第二步:React组件实现滚动监听与加载逻辑
组件需要完成三个核心动作:首次加载第一页数据、监听滚动事件判断是否触底、触底时触发下一页加载(还要避免重复请求)。
示例组件
// components/ScrollList.js import React, { useEffect, useRef } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { fetchList } from '../actions/listActions'; const ScrollList = () => { const dispatch = useDispatch(); const { data, currentPage, pageSize, isLoading, hasMore } = useSelector(state => state.list); const listContainerRef = useRef(null); // 首次加载第一页数据 useEffect(() => { dispatch(fetchList(1, pageSize)); }, [dispatch, pageSize]); // 监听滚动事件 useEffect(() => { const handleScroll = () => { // 正在加载或没有更多数据时,直接返回 if (isLoading || !hasMore) return; const container = listContainerRef.current; // 提前100px触发加载(提升用户体验,避免等待) const isNearBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 100; if (isNearBottom) { dispatch(fetchList(currentPage, pageSize)); } }; const container = listContainerRef.current; container.addEventListener('scroll', handleScroll); // 组件卸载时移除监听 return () => container.removeEventListener('scroll', handleScroll); }, [dispatch, currentPage, pageSize, isLoading, hasMore]); return ( <div ref={listContainerRef} style={{ height: '80vh', overflowY: 'auto', border: '1px solid #eee' }} > {data.map(item => ( <div key={item.id} style={{ padding: '16px', borderBottom: '1px solid #f0f0f0' }}> {/* 替换成你的列表项内容 */} <h4>{item.title}</h4> <p>{item.description}</p> </div> ))} {/* 加载状态提示 */} {isLoading && <div style={{ textAlign: 'center', padding: '16px', color: '#666' }}>加载中...</div>} {/* 无更多数据提示 */} {!hasMore && !isLoading && <div style={{ textAlign: 'center', padding: '16px', color: '#999' }}>已加载全部数据</div>} </div> ); }; export default ScrollList;
第三步:关键注意事项
- 防止重复请求:必须在滚动监听里判断
isLoading状态,避免用户快速滚动时触发多次重复请求 - 提前加载优化:不要等到完全滚动到底部才触发请求,提前100px左右触发,让用户几乎感知不到加载等待
- 边界判断:当API返回的数据量小于每页条数时,把
hasMore设为false,停止后续加载逻辑 - 进阶性能优化:如果后续列表总条数还是很大,可以配合
React.memo优化列表项渲染,或者用react-window实现虚拟列表,进一步减少DOM节点数量 - 错误处理:记得处理API请求失败的场景,比如给用户显示错误提示,或者提供重试按钮
这套方案落地后,就能实现首次加载20条、滚动触底自动加载下20条的效果,彻底解决一次性加载700+条的性能问题,加载耗时会降到单次API请求的时间,用户体验会提升很多。
内容的提问来源于stack exchange,提问作者Sharad Pawar
相关产品推荐
相关产品推荐

