如何在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
相关产品推荐
相关产品推荐

