React ag-grid服务端全局筛选出现无限循环问题求助
React AG-Grid服务端全局筛选无限循环问题修复
问题描述
实现React AG-Grid服务端全局筛选时,出现无限循环:搜索结果能正常展示,但搜索过程中组件持续向服务器发起请求,页面不断刷新,结果下方始终显示加载动画。参考官方快速筛选示例修改后问题仍存在,组件代码如下:
export const AgSpaces = () => { const { workspaceId } = useParams(); const gridRef = useRef<AgGridReact>(null); const { theme } = useThemeStore(); const [searchParam, setSearchParam] = useState(""); const [colDefs] = useState<ColDef[]>([ { field: "id", headerName: "ID" }, { field: "name", headerName: "Name" }, { field: "groupCount", headerName: "Group Count", sortable: true }, { field: "listCount", headerName: "List Count", sortable: true }, { field: "description", headerName: "Description", valueGetter: (params) => params.data.description || "-", }, { field: "color", headerName: "Color", cellRenderer: (params: { value: string }) => { const color = params.value; if (!color) return null; return ( <div className="w-5 h-5 border border-gray-300 rounded" style={{ backgroundColor: params.value }} ></div> ); }, }, { field: "createdAt", headerName: "Created At", valueFormatter: (params) => { const date = params.value; return date ? dateFormat(date) : ""; }, }, { field: "updatedAt", headerName: "Updated At", valueFormatter: (params) => { const date = params.value; return date ? dateFormat(date) : ""; }, }, ]); const datasource = (workspaceId: string) => { return { getRows: async (params: IServerSideGetRowsParams) => { const { startRow, endRow, sortModel } = params.request; // Process the sortModel for a single column sorting let sort = sortModel?.[0]; // Assuming there's only one sort model let sortParam = sort ? `${sort.colId}:${sort.sort}` : undefined; // Convert to "column:order" format // Fetch data based on startRow, endRow, sort, and search const response = await fetchSpacesByWorkspaceId( workspaceId ?? "", "groupCount,listCount", startRow ? startRow + 1 : 1, endRow, sortParam, // Pass the single sort string searchParam ); if (response) { params.success({ rowData: response.spaces, rowCount: response.total, // Set total row count for pagination }); } else { params.fail(); } }, }; }; const onGridReady = useCallback((event: GridReadyEvent) => { event.api.resetColumnState(); }, []); return ( <div> <div className=""> <span>Quick Filter:</span> <input type="text" id="filter-text-box" placeholder="Filter..." onChange={(e)=> setSearchParam(e.target.value)} className="rounded m-3 p-1 border bg-background" /> </div> <div className={`${ theme === "dark" ? "ag-theme-quartz-dark" : "ag-theme-quartz" }`} style={{ height: 500 }} > <AgGridReact onGridReady={onGridReady} columnDefs={colDefs} rowModelType="serverSide" pagination={true} // Enable pagination paginationPageSize={20} // Rows per page cacheBlockSize={20} // Rows fetched in each request (sync with paginationPageSize) animateRows={true} serverSideDatasource={datasource(workspaceId as string)} //onPaginationChanged={onPaginationChanged} // Listen for page size changes paginationPageSizeSelector={[20, 50, 100, 1000, 10000]} defaultColDef={{ filter: true }} /> </div> </div> ); };
问题根源
- 数据源重复创建:每次组件渲染(比如
searchParam变化时),serverSideDatasource都会传入新创建的数据源对象。AG-Grid检测到数据源更新后,会自动触发数据重新加载,进而引发组件再次渲染,形成循环。 - 状态依赖未正确管理:
getRows中直接引用searchParam状态,但数据源没有缓存,状态变化会直接触发新数据源生成,加剧循环。
修复步骤
1. 用useMemo缓存数据源
通过useMemo缓存服务端数据源,仅在依赖项变化时重新生成,避免不必要的数据源创建:
// 替换原来的datasource函数 const serverSideDatasource = useMemo(() => { return { getRows: async (params: IServerSideGetRowsParams) => { const { startRow, endRow, sortModel } = params.request; let sort = sortModel?.[0]; let sortParam = sort ? `${sort.colId}:${sort.sort}` : undefined; const response = await fetchSpacesByWorkspaceId( workspaceId ?? "", "groupCount,listCount", startRow ? startRow + 1 : 1, endRow, sortParam, searchParam ); if (response) { params.success({ rowData: response.spaces, rowCount: response.total, }); } else { params.fail(); } }, }; }, [workspaceId, searchParam]); // 依赖项包含workspaceId和searchParam
2. 更新AG-Grid的数据源属性
将serverSideDatasource={datasource(workspaceId as string)}替换为缓存后的数据源:
<AgGridReact onGridReady={onGridReady} columnDefs={colDefs} rowModelType="serverSide" pagination={true} paginationPageSize={20} cacheBlockSize={20} animateRows={true} serverSideDatasource={serverSideDatasource} // 使用缓存后的数据源 paginationPageSizeSelector={[20, 50, 100, 1000, 10000]} defaultColDef={{ filter: true }} />
3. 可选:添加搜索防抖(优化体验)
为了避免输入过程中频繁发起请求,可以给搜索输入添加防抖处理:
// 引入防抖函数(可使用Lodash的debounce,或自行实现) import { debounce } from 'lodash'; // 在组件内定义防抖后的状态更新函数 const debouncedSetSearchParam = useCallback( debounce((value: string) => setSearchParam(value), 300), [] ); // 修改输入框的onChange事件 <input type="text" id="filter-text-box" placeholder="Filter..." onChange={(e) => debouncedSetSearchParam(e.target.value)} className="rounded m-3 p-1 border bg-background" />
内容的提问来源于stack exchange,提问作者mercury
相关产品推荐
相关产品推荐

