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

Material UI DataGrid结合SWR服务端分页时数据闪烁问题排查

在Material UI DataGrid中使用SWR实现服务端分页时的数据闪烁问题解决

问题根源

这是SWR核心缓存策略导致的:SWR默认采用stale-while-revalidate模式——优先返回本地缓存的旧数据,同时后台发起请求更新数据。当你新增记录后,已浏览页面的缓存数据、总行数依然是旧值,切换回这些页面时,SWR会先渲染缓存的旧内容,等新请求完成后才替换成最新数据,从而产生“闪烁”。

而原useEffect方案没有缓存机制,每次切换页面都会直接发起新请求,自然不会出现这种先旧后新的视觉差异。

优雅解决方案

1. 新增记录后主动更新缓存(推荐)

在新增记录成功后,调用SWR的mutate方法主动同步相关缓存,确保后续切换页面时读取的是最新数据:

import { useSWRConfig } from 'swr';

// 假设列表数据请求key格式为`/api/data?page=${page}`,总行数请求key为`/api/total`
const { mutate } = useSWRConfig();

const handleAddRecord = async (newRecord) => {
  // 调用新增接口
  await api.addRecord(newRecord);
  
  // 先更新总行数缓存,触发重新请求
  await mutate('/api/total');
  
  // 让所有分页数据的缓存失效并重新验证
  mutate((key) => typeof key === 'string' && key.startsWith('/api/data?page='), undefined, { revalidate: true });
};

这种方式既保留了SWR的缓存优势,又能在数据变更后及时同步缓存,从根源上避免闪烁。

2. 优化加载状态,降低用户感知

如果不想修改缓存逻辑,可以通过加载状态提示用户后台正在更新数据,掩盖旧数据的短暂显示:

import { CircularProgress } from '@mui/material';
import { useSWR } from 'swr';

const MyDataGrid = () => {
  const [page, setPage] = useState(0);
  const { data: total } = useSWR('/api/total', fetcher);
  const { data: rows, isLoading, isValidating } = useSWR(`/api/data?page=${page}`, fetcher);

  return (
    <div style={{ position: 'relative', height: 400 }}>
      <DataGrid
        rows={rows?.data || []}
        columns={columns}
        paginationMode="server"
        rowCount={total || 0}
        onPageChange={(_, newPage) => setPage(newPage)}
        loading={isLoading}
      />
      {/* 当SWR正在重新验证时,显示加载遮罩 */}
      {isValidating && (
        <div style={{
          position: 'absolute',
          inset: 0,
          backgroundColor: 'rgba(255,255,255,0.8)',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center'
        }}>
          <CircularProgress size={24} />
        </div>
      )}
    </div>
  );
};

当SWR在后台更新数据时,用户会看到加载状态,不会注意到旧数据的闪烁。

3. 绑定缓存key与总行数

把总行数作为分页请求缓存key的一部分,这样总行数变化时,所有分页的缓存key都会失效,SWR会自动重新请求最新数据:

const { data: total } = useSWR('/api/total', fetcher);
// 将total加入缓存key,total变化则key变化,缓存自动失效
const { data: rows } = useSWR(total ? `/api/data?page=${page}&total=${total}` : null, fetcher);

这种方式无需手动调用mutate,但缺点是如果总行数频繁变化,会降低缓存命中率,影响性能。

4. 针对特定页面禁用缓存优先

如果某些页面要求绝对实时,可以给对应的SWR请求配置revalidateOnMount: true,强制组件挂载时重新验证数据:

const { data: rows } = useSWR(`/api/data?page=${page}`, fetcher, {
  revalidateOnMount: true,
});

但这种方式会失去缓存带来的性能提升,仅适合对实时性要求极高的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:11:16