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

如何在MUI DataGrid中实现无限滚动(保留原组件)

解决MUI DataGrid结合GraphQL useLazyQuery的无限滚动问题

核心问题分析

  1. 当前setStyles直接替换数据而非追加,导致新页数据覆盖旧数据
  2. useLazyQuery的变量未动态更新,page变化时无法传递正确的cursor
  3. 缺少滚动到底部触发加载下一页的逻辑

分步解决方案

1. 调整数据更新逻辑(StylesHOC)

  • 重置搜索条件时清空现有数据,新页数据追加到现有数组
  • 修改useLazyQuery的onCompleted回调,改为追加数据:
onCompleted: (data) => {
  const newCursor = data.styles?.edges.at(-1)?.cursor;
  if (newCursor && !cursors.map((c) => c.cursor).includes(newCursor)) {
    setCursors(prev => [...prev, { pageIndex: page + 1, cursor: newCursor }]);
  }
  totalCountDispatch({ type: 'update_totalCount', newTotalCount: data.styles?.totalCount ?? 0 });
  // 追加新数据而非替换
  setStyles(prev => [
    ...prev,
    ...(data.styles?.edges
      .map((edge) => edge.node)
      .filter((node) => !pinnedStyles.includes(node.styleNumber)) || [])
  ]);
},
  • 在重置逻辑的useEffect中添加清空数据的操作:
useEffect(() => {
  setPage(0);
  setCursors([{ pageIndex: 0, cursor: '' }]);
  setStyles([]); // 重置时清空现有数据
  if (selectedBrands.length !== 0) {
    // 调用getStyles时传入最新变量
    getStyles({
      variables: {
        brandNumber: selectedBrands[0],
        collectionNumber: collectionsFilter?.collectionNumber,
        productLineNumber: productLinesFilter?.productLineNumber,
        purchaseDivisionNumber: purchaseDivisionsFilter?.purchaseDivisionNumber,
        generalQuery: searchQuery,
        pagination: { first: size, after: '' },
      },
    });
  }
}, [selectedBrands, searchQuery, collectionsFilter, productLinesFilter, purchaseDivisionsFilter, getStyles]);

2. 动态传递GraphQL查询变量

将useLazyQuery的变量从初始配置中移除,改为调用时传入,确保每次查询都使用最新的page和cursor:

// 重新定义useLazyQuery,移除静态variables
const [getStyles, { loading: getStylesQueryLoading, error: getStylesQueryError }] = useLazyQuery(
  graphQlService.getAllStyles,
  {
    pollInterval: 0,
    fetchPolicy: 'network-only',
    context: { clientName: GraphClientNames['blabla'] },
    // onCompleted和onError保持修改后的逻辑
    // ...
  }
);

3. 实现加载下一页方法并注入Context

在StylesHOC中添加loadNextPage方法,用于触发下一页数据加载,并注入到Context中供DataGrid组件使用:

const loadNextPage = () => {
  const nextPage = page + 1;
  // 检查是否还有未加载的数据
  if (!getStylesQueryLoading && styles.length < totalCount) {
    setPage(nextPage);
    getStyles({
      variables: {
        brandNumber: selectedBrands[0],
        collectionNumber: collectionsFilter?.collectionNumber,
        productLineNumber: productLinesFilter?.productLineNumber,
        purchaseDivisionNumber: purchaseDivisionsFilter?.purchaseDivisionNumber,
        generalQuery: searchQuery,
        pagination: {
          first: size,
          after: cursors.find(c => c.pageIndex === nextPage)?.cursor ?? cursors.at(-1)?.cursor ?? '',
        },
      },
    });
  }
};

// 更新contextValue,加入loadNextPage
const contextValue = useMemo(
  () => ({
    styles,
    isStylesLoading: getStylesQueryLoading,
    stylesError: getStylesQueryError,
    totalCount,
    cursors,
    size,
    page,
    setPage,
    updateStyles,
    loadNextPage, // 新增加载下一页方法
  }),
  [cursors, getStylesQueryError, getStylesQueryLoading, page, styles, totalCount, updateStyles, loadNextPage]
);

4. 配置DataGrid触发无限滚动

在DataGrid组件中添加滚动监听,当滚动接近底部时触发加载下一页:

<DataGrid
  rows={tableRows}
  columns={columns}
  loading={isStylesLoading || !tableRows}
  localeText={localeText}
  rowCount={totalCount}
  getRowClassName={(params: GridRowParams): string => {
    const isPinnedRow = params.row.id.includes('pinned');
    return `${classes.row} ${isPinnedRow ? classes.pinnedRow : ''}`;
  }}
  rowHeight={64}
  paginationMode="server"
  pageSize={size}
  page={page} // 绑定当前页码
  classes={{
    root: classes.root,
    columnHeaders: classes.columnHeaders,
    columnHeaderTitle: classes.columnHeaderTitle,
    cell: classes.cell,
    columnSeparator: classes.columnSeparator,
    footerContainer: classes.footerContainer,
  }}
  components={{
    LoadingOverlay,
    Pagination: () => null,
  }}
  componentsProps={{
    row: {
      onMouseEnter: handleRowMouseEnter,
      onMouseLeave: handleRowMouseLeave,
    },
  }}
  disableSelectionOnClick
  disableColumnMenu
  onCellClick={handleCellClick}
  // 添加滚动监听触发加载下一页
  onScroll={(event) => {
    const grid = event.currentTarget;
    // 距离底部100px时触发加载
    if (grid.scrollTop + grid.clientHeight >= grid.scrollHeight - 100) {
      loadNextPage();
    }
  }}
/>

关键注意事项

  • 避免重复加载:在loadNextPage中判断当前是否处于加载状态,防止多次触发请求
  • 边界处理:检查styles.length < totalCount,确保没有超出总数据量
  • 变量依赖:确保useEffect和loadNextPage的依赖数组包含所有相关变量,避免闭包问题

内容的提问来源于stack exchange,提问作者Jdeveloper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 11:02:32