You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
  );
};

问题根源

  1. 数据源重复创建:每次组件渲染(比如searchParam变化时),serverSideDatasource都会传入新创建的数据源对象。AG-Grid检测到数据源更新后,会自动触发数据重新加载,进而引发组件再次渲染,形成循环。
  2. 状态依赖未正确管理: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:14:52