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

如何在Apollo客户端缓存中获取Reactive Var值?图片上传乐观加载实现

实现Apollo图片上传乐观响应与加载状态的最佳实践

你的核心思路没问题——用乐观响应展示base64图、自定义缓存字段控制加载状态是正确的方向,直接在字段策略中读取reactiveVar也是Apollo官方认可的做法,只是需要调整细节来适配多图片场景并修正字段策略的写法。

核心实现步骤

1. 用reactiveVar追踪单/多图片加载状态

因为可能存在多张图片同时上传的情况,建议用对象存储每个图片ID对应的加载状态,避免单个布尔值无法区分不同图片的问题:

import { makeVar } from '@apollo/client';

// 初始为空对象,键为图片ID,值为加载状态(true/false)
export const imageLoadingStateVar = makeVar({});

2. 配置缓存字段策略,自定义imageLoading字段

在InMemoryCache的类型策略中,为Image类型添加imageLoading字段,通过read函数读取reactiveVar的对应状态:

const cache = new InMemoryCache({
  typePolicies: {
    Image: {
      fields: {
        // 自定义加载状态字段
        imageLoading: {
          read(_, { readField }) {
            // 获取当前Image对象的ID,匹配reactiveVar中的状态
            const imageId = readField('id');
            return imageLoadingStateVar()[imageId] || false;
          }
        },
        // 可选:配置url字段的合并策略,优先使用乐观响应的base64值
        url: {
          merge(existing, incoming) {
            return incoming || existing;
          }
        }
      }
    }
  }
});

3. 上传Mutation中结合乐观响应与状态更新

在上传时生成临时ID、写入乐观响应的base64图片,同时通过reactiveVar标记加载状态;上传完成/失败后更新状态:

import { gql, useMutation } from '@apollo/client';

const UPLOAD_IMAGE = gql`
  mutation UploadImage($file: Upload!) {
    uploadImage(file: $file) {
      id
      url
      name
    }
  }
`;

// 文件转base64的辅助函数
const fileToBase64 = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
};

// 上传逻辑
const useImageUpload = () => {
  const [uploadMutation] = useMutation(UPLOAD_IMAGE);

  const upload = async (file) => {
    const base64Url = await fileToBase64(file);
    const tempImageId = `temp-${Date.now()}`; // 生成临时ID用于乐观响应

    // 标记当前临时图片为加载中
    imageLoadingStateVar({
      ...imageLoadingStateVar(),
      [tempImageId]: true
    });

    try {
      const { data } = await uploadMutation({
        variables: { file },
        optimisticResponse: {
          __typename: 'Mutation',
          uploadImage: {
            __typename: 'Image',
            id: tempImageId,
            url: base64Url,
            name: file.name
          }
        }
      });

      // 上传成功:标记真实图片ID为加载完成,移除临时ID的状态
      imageLoadingStateVar({
        ...imageLoadingStateVar(),
        [tempImageId]: false,
        [data.uploadImage.id]: false
      });
    } catch (err) {
      // 上传失败:标记临时图片为加载完成,可额外处理错误状态
      imageLoadingStateVar({
        ...imageLoadingStateVar(),
        [tempImageId]: false
      });
      console.error('图片上传失败:', err);
    }
  };

  return { upload };
};

4. UI中读取缓存字段展示状态

在组件中通过Query获取图片数据,直接读取imageLoading字段控制加载器显示:

import { useQuery } from '@apollo/client';

const GET_IMAGES = gql`
  query GetImages {
    images {
      id
      url
      name
      imageLoading # 读取自定义缓存字段
    }
  }
`;

const ImageList = () => {
  const { data } = useQuery(GET_IMAGES);

  return (
    <div className="image-list">
      {data?.images.map(img => (
        <div key={img.id} className="image-item">
          {img.imageLoading && <div className="loading-spinner">上传中...</div>}
          <img src={img.url} alt={img.name} />
        </div>
      ))}
    </div>
  );
};

为什么这种方式可行?

  • reactiveVar是Apollo官方推荐的本地状态管理方案,天生支持响应式更新,和缓存字段策略结合能自动触发UI重渲染
  • 按图片ID追踪状态,完美支持多图片同时上传的场景
  • 乐观响应直接写入缓存的base64图片,保证上传时立即反馈,上传完成后会被真实URL自动覆盖

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 09:09:21