React ag-grid客户端行模型实现滚动底部非冻结加载器咨询
实现客户端行模型下的滚动触发自定义加载提示
我之前也碰到过一模一样的需求,官方的showLoadingOverlay()确实会锁死整个网格,完全不符合滚动加载的交互预期。下面是我摸索出来的实现方案,完全基于客户端行模型,不需要切换到其他行模型:
核心思路
通过监听网格滚动事件,在滚动到第99行附近时触发数据加载,同时在网格底部插入一个全屏的自定义加载行(而非覆盖整个网格的overlay),加载完成后移除该行并追加新数据,全程不阻塞网格的正常交互。
具体实现步骤
1. 初始化状态与网格引用
首先在React组件中维护必要的状态,并保存网格API的引用:
import { useState, useRef, useCallback } from 'react'; import { AgGridReact } from 'ag-grid-react'; import 'ag-grid-community/styles/ag-grid.css'; import 'ag-grid-community/styles/ag-theme-alpine.css'; import _ from 'lodash'; const MyGrid = () => { const [rowData, setRowData] = useState([]); const [isLoading, setIsLoading] = useState(false); const [hasMoreData, setHasMoreData] = useState(true); // 标记是否还有更多数据可加载 const gridApiRef = useRef(null); // 网格就绪时保存API引用 const onGridReady = (params) => { gridApiRef.current = params.api; // 加载初始数据 loadInitialData(); };
2. 配置网格与自定义加载行渲染
在网格配置中,启用全屏行(fullWidthRow)并自定义加载行的渲染内容:
const gridOptions = { getRowNodeId: (data) => data.id, // 必须设置,方便后续精准移除加载行 onBodyScroll: throttledHandleScroll, // 节流后的滚动事件处理 fullWidthCellRenderer: (params) => { // 判断是否为加载行,渲染自定义spinner和提示文本 if (params.data.isLoading) { return ( <div style={{ padding: '20px', textAlign: 'center' }}> <div className="spinner"></div> <span>One moment please</span> </div> ); } return null; }, // 你的其他网格配置(列定义、高度等) columnDefs: [ { headerName: 'ID', field: 'id' }, { headerName: '名称', field: 'name' }, // 其他业务列... ], };
3. 滚动事件处理与节流
为了避免滚动时频繁触发加载请求,对滚动事件做节流处理,并判断是否到达触发加载的位置:
// 节流处理滚动事件(300ms内只触发一次,避免频繁调用API) const throttledHandleScroll = useCallback( _.throttle(() => { if (isLoading || !hasMoreData) return; const api = gridApiRef.current; if (!api) return; // 根据行索引判断:第99行之后触发(索引从0开始,所以判断>=98) const lastDisplayedRow = api.getLastDisplayedRow(); if (lastDisplayedRow >= 98) { loadMoreData(); } // 也可以用滚动距离判断(更通用,适合任意初始数据量) // const gridBody = api.getGridBodyElement(); // const scrollThreshold = 50; // 可根据行高调整阈值 // if (gridBody.scrollTop + gridBody.clientHeight >= gridBody.scrollHeight - scrollThreshold) { // loadMoreData(); // } }, 300), [isLoading, hasMoreData] );
4. 数据加载逻辑
实现加载函数,添加临时加载行、调用API、更新数据:
const loadMoreData = async () => { setIsLoading(true); // 添加全屏加载行到数据中 setRowData(prev => [...prev, { id: 'loading-row', fullWidth: true, isLoading: true }]); try { // 调用你的API,替换成实际接口请求 const newData = await fetch('/api/your-data-endpoint', { method: 'GET', // 传递分页参数,比如当前数据长度作为偏移量 params: { offset: rowData.length - 1 } // 减去加载行的数量 }).then(res => res.json()); // 移除加载行,追加新数据 setRowData(prev => prev.filter(row => row.id !== 'loading-row').concat(newData)); // 如果返回数据为空,标记没有更多数据 if (newData.length === 0) { setHasMoreData(false); } } catch (error) { console.error('加载数据失败:', error); // 加载失败时移除加载行 setRowData(prev => prev.filter(row => row.id !== 'loading-row')); } finally { setIsLoading(false); } }; // 初始数据加载函数 const loadInitialData = async () => { // 加载第一页数据,逻辑类似loadMoreData const initialData = await fetch('/api/initial-data').then(res => res.json()); setRowData(initialData); };
5. 添加Spinner样式
在组件的CSS中添加加载动画样式:
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto 8px; }
6. 渲染网格
最后渲染AgGridReact组件:
return ( <div className="ag-theme-alpine" style={{ height: 600, width: '100%' }}> <AgGridReact rowData={rowData} gridOptions={gridOptions} onGridReady={onGridReady} /> </div> ); }; export default MyGrid;
关键注意点
- 必须设置
getRowNodeId,否则无法准确移除临时的加载行。 - 对滚动事件做节流处理,避免短时间内多次触发API请求。
- 通过
fullWidthCellRenderer实现的加载行只会占据网格底部,不会阻塞整个网格的交互。
内容的提问来源于stack exchange,提问作者sandy
相关产品推荐
相关产品推荐

