1

I'm facing an issue where applying filters in my AG Grid with server-side row grouping is triggering multiple API calls. In some cases, it even results in continuous API calls.

This only happens when row grouping is enabled. I have another component with the same server-side setup but without row grouping, and that works perfectly—only one API call is triggered per filter change.

From debugging, I noticed that the useEffect responsible for calling refreshServerSide() is being triggered only once, but the actual issue seems to lie inside the getServerSideDatasource function. Specifically, params.request is returned twice when I apply a single filter, resulting in two API calls.

Here is the relevant part of my component:

const memoizedFilters = useMemo(() => filters, [JSON.stringify(filters)]);

useEffect(() => {
 if(filtersRef.current !== filters) {
 filtersRef.current = filters;
  if (gridApiRef.current && !gridApiRef.current.isDestroyed()) {
   console.log("Refreshing server-side data with new filters");
  
    gridApiRef.current.refreshServerSide();
  }
  }
}, [memoizedFilters]);

const autoGroupColumnDef = useMemo<ColDef>(() => ({
  headerName: "Group",
  cellRendererParams: {
    suppressCount: false,
    suppressDoubleClickExpand: false,
    innerRenderer: () => "",
  },
}), []);
const getServerSideDatasource = (): IServerSideDatasource => {
 return {
   getRows: async (params: IServerSideGetRowsParams) => {
     console.log("Requesting rows", params.request);
    
     const newStartRow = params.request.startRow ?? 0;
     const newEndRow = params.request.endRow ?? 10;

     if (params.request.groupKeys.length && params.request.groupKeys[0] !== "") {
       const campaign_id = params.request.groupKeys[0];
       const campaignFilters = filtersRef.current;

       try {
         const res = await fetchCampaignOverview({ startRow: newStartRow, endRow: newEndRow, campaign_id, ...campaignFilters });
         if (res.rows.length) {
           params.success({ rowData: res.rows, rowCount: res.totalRows });
         } else {
           params.success({ rowData: [], rowCount: 0 });
         }
       } catch (error) {
         console.error("Error fetching rows:", error);
         params.fail();
       }
     } else if (params.request.groupKeys[0] === "") {
      
       const unGroupedData = fetchedDataRef.current.filter((item) => item.campaign_id === "");
       console.log("Fetching ungrouped data", unGroupedData);
       params.success({ rowData: unGroupedData, rowCount: unGroupedData.length });
     } else {
       try {
         const campaignFilters = filtersRef.current;
         const res = await fetchCampaignOverview({ startRow: newStartRow, endRow: newEndRow, ...campaignFilters });
         if (res.rows.length) {
           const unbalancedData = res.rows.map((item) => ({
             ...item,
             campaign_id: item.has_child ? item.campaign_id : "",
           }));

           fetchedDataRef.current = unbalancedData;
           const groupedData = unbalancedData.filter((item) => item.campaign_id !== "");
           const unGroupedData = unbalancedData.filter((item) => item.campaign_id === "");
           if (unGroupedData.length > 0) groupedData.push(unGroupedData[0]);
           params.success({ rowData: groupedData, rowCount: res.totalRows });
         } else {
           params.success({ rowData: [], rowCount: 0 });
         }
       } catch (error) {
         params.fail();
         console.error("Error fetching rows:", error);
       }
     }
   },
 };
};

const onGridReady =  (params: GridReadyEvent) => {
 console.log("Grid is ready");
  if (!gridApiRef.current || gridApiRef.current.isDestroyed()) {
    gridApiRef.current = params.api
    const datasource = getServerSideDatasource();
    params.api.setGridOption("serverSideDatasource", datasource);
  }
};


 
 const paginationPageSizeSelector = useMemo(() => [10, 20, 50, 100], []);

return (
  <>
    <CampaignFilters
      onFilterChange={(filters: CampaignFilter) => { setFilters(filters); }}
    />
    <TableContainer>
      <AgGridReact
        autoGroupColumnDef={autoGroupColumnDef}
        cacheBlockSize={paginationPageSize}
        columnDefs={colDefs}
        defaultColDef={defaultColDef}
        domLayout="autoHeight"
        blockLoadDebounceMillis={1000}
       //  getServerSideGroupKey={(params: { data: CampaignOverviewRowData }) => params.data.campaign_id || ""}
        groupAllowUnbalanced={true}
        onGridReady={onGridReady}
        onPaginationChanged={() => {
         if (gridApiRef.current) {
             const pageSize = gridApiRef.current.paginationGetPageSize();
             setPaginationPageSize(pageSize);
             }
           }}
        paginateChildRows={true}
        pagination={true}
        paginationPageSize={10}
        paginationPageSizeSelector={paginationPageSizeSelector}
        rowModelType="serverSide"
      />
    </TableContainer>
  </>
);
};

What I’ve Tried:

Checked that refreshServerSide() is only triggered once.

Verified that params.request is returned twice on filter change (causing two API calls).

Compared it with a component that uses the same filtering logic without row grouping, which works fine.

Expected Behavior: Only one API call should be made when applying a filter.

Actual Behavior: Two or more API calls are made when applying a single filter, and in some cases, it continues to call the API indefinitely.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.