如何正确向Apollo缓存的分页字段值中添加新对象
解决方案
报错原因
你通过readQuery从Apollo Client缓存中读取的所有数据对象默认会被Object.freeze冻结,禁止直接修改以避免污染缓存。你之前只对comments对象做了浅拷贝,内部的edges数组仍然指向缓存中被冻结的原数组,所以调用push方法时会抛出不可扩展的错误。
最佳实现方案
Apollo Client原生支持Optimistic UI,官方推荐搭配optimisticResponse+update回调实现,无需手动处理请求中状态、成功/失败回滚逻辑:
optimisticResponse会在请求发起时立刻返回你构造的假响应数据update回调会用这份假数据更新缓存,立即渲染新评论- 若请求成功,Apollo会自动用服务端返回的真实数据替换缓存中的假数据;若请求失败,会自动回滚缓存变更,无需额外处理
完整代码示例
import { useMutation, gql } from '@apollo/client'; // 你的创建评论Mutation const CREATE_COMMENT_MUTATION = gql` mutation CreateComment($articleId: ID!, $text: String!) { createComment(articleId: $articleId, text: $text) { id text liked likes createdAt owner { id firstName lastName username numericId } } } `; const ArticleDetail = ({ articleId, currentUser }) => { const [createComment, { loading }] = useMutation(CREATE_COMMENT_MUTATION, { // 构造乐观响应 optimisticResponse: (variables) => ({ __typename: 'Mutation', createComment: { __typename: 'Comment', id: `temp-${Date.now()}`, // 临时唯一ID,服务端返回后会自动替换 text: variables.text, liked: false, likes: 0, createdAt: new Date().toISOString(), owner: { __typename: 'UserBase', ...currentUser } } }), // 更新缓存逻辑 update: (cache, { data: { createComment } }) => { // 读取缓存中的文章数据 const { getArticle } = cache.readQuery({ query: GET_ARTICLE_QUERY, variables: { id: articleId } }); // 构造新的评论分页数据,所有修改的字段都要新建引用 const updatedComments = { ...getArticle.comments, totalCount: getArticle.comments.totalCount + 1, pageInfo: { ...getArticle.comments.pageInfo, count: getArticle.comments.pageInfo.count + 1, // 若新评论插在列表末尾,更新endCursor;插在开头则更新startCursor endCursor: btoa(createComment.id) // Cursor格式根据你后端规则调整,通常是Base64编码的评论ID }, edges: [ ...getArticle.comments.edges, // 新建数组,避免修改原冻结数组 { __typename: 'CommentEdge', node: createComment, cursor: btoa(createComment.id) } ] }; // 将更新后的数据写回缓存 cache.writeQuery({ query: GET_ARTICLE_QUERY, variables: { id: articleId }, data: { getArticle: { ...getArticle, comments: updatedComments } } }); } }); // 提交评论的触发逻辑示例 const handleSubmitComment = (text) => { createComment({ variables: { articleId, text } }); }; // 其余组件逻辑省略 }
注意事项
- 不要直接修改从缓存读取的任何对象/数组,所有需要变更的层级都要通过展开运算符新建引用即可,不需要全量深拷贝,只会浪费性能
- 临时ID保证唯一即可,不需要和后端ID规则一致,Apollo会自动在请求完成后替换为真实ID
- 如果你的评论列表是最新评论排在最前,把新edge放在edges数组的开头,同时更新
pageInfo.startCursor即可
内容的提问来源于stack exchange,提问作者Velidan
相关产品推荐
相关产品推荐

