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

对比新旧对象数组更新状态,Apollo增删后重查引发全量渲染问题

优化Apollo Mutation后的列表重渲染问题

嗨,这个问题我之前在项目里也踩过坑!用RefetchQueries确实会触发全量数据拉取,导致整个列表的所有项都重新渲染——明明只是新增了一条数据,却要把旧的都重新渲染一遍,实在没必要。其实Apollo Client提供了更精准的缓存更新方式,能完美解决这个问题,下面给你一步步讲怎么优化:

1. 用Mutation的update函数手动更新缓存(核心方案)

RefetchQueries的本质是重新发起Query请求,替换整个缓存数据,所以React会认为整个列表数组变了,进而重渲染所有项。而用update函数,我们可以直接在缓存里修改数据,不用发起新的网络请求,而且只更新需要变化的部分。

举个实际的代码例子:

首先定义你的Query和Mutation:

# 获取名称列表的Query
const GET_NAMES = gql`
  query GetNames {
    names {
      id
      name
    }
  }
`;

# 添加名称的Mutation
const ADD_NAME = gql`
  mutation AddName($name: String!) {
    addName(name: $name) {
      id
      name
    }
  }
`;

然后在使用useMutation的时候,加上update函数:

const [addName] = useMutation(ADD_NAME, {
  update(cache, { data: { addName: newName } }) {
    // 1. 先从缓存里读取现有的名称列表
    const { names } = cache.readQuery({ query: GET_NAMES });
    
    // 2. 把新添加的项合并到旧列表里(注意要返回新数组,保持immutable)
    cache.writeQuery({
      query: GET_NAMES,
      data: { names: [...names, newName] },
    });
  },
  // 这里直接删掉RefetchQueries,不需要再全量拉取了!
  // refetchQueries: [{ query: GET_NAMES }],
});

这样操作后,Apollo会直接更新缓存中的列表,组件会感知到缓存变化,但只有新增的项对应的组件会重新渲染——因为旧项的对象引用没有变。

2. 给列表项组件加上React.memo(确保旧项不重渲染)

光更新缓存还不够,如果你列表项组件没有做优化,React可能还是会重渲染所有项。这时候需要用React.memo包裹列表项组件,让它只在props真正变化的时候才重新渲染:

import React from 'react';

// 用React.memo包裹,组件会浅比较props,只有变化时才渲染
const NameItem = React.memo(({ name, id }) => {
  console.log(`渲染:${name}`); // 测试用,新增时只有新名字会打印
  return <div key={id}>{name}</div>;
});

如果是类组件的话,可以继承PureComponent,效果是一样的。

3. 确认Query的fetchPolicy(可选,默认已满足)

确保你的useQuery使用的是默认的fetchPolicy: 'cache-first',这样组件会优先从缓存读取数据,不会在缓存更新后又发起网络请求。如果之前改过这个配置,记得改回来:

const { data } = useQuery(GET_NAMES, {
  fetchPolicy: 'cache-first', // 这是默认值,可写可不写
});

额外说明:删除操作的类似优化

如果是删除名称的场景,同样可以用update函数来处理,只需要过滤掉要删除的项:

const [deleteName] = useMutation(DELETE_NAME, {
  update(cache, { data: { deleteName: deletedId } }) {
    const { names } = cache.readQuery({ query: GET_NAMES });
    cache.writeQuery({
      query: GET_NAMES,
      data: { names: names.filter(item => item.id !== deletedId) },
    });
  },
});

这样调整之后,不管是新增还是删除,都只会影响对应的列表项,旧的项完全不会重新渲染,性能一下就上来了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:00:49