乐观突变结果
在服务器响应之前更新你的 UI
通常可以预测 突变 的最有可能的结果 在 你的 GraphQL 服务器 返回之前。 Apollo 客户端 可以使用这个“最有可能的结果”以乐观的方式更新你的 UI ,让你的应用对用户更具有响应性。
例如,假设我们有一个支持以下 mutation 的博客应用:
type Mutation {updateComment(commentId: ID!, content: String!): Comment!# ...other mutations...}
如果用户编辑了博客上的现有评论,应用程序会执行updateComment
突变操作,它会返回一个更新了内容Comment
对象。
我们知道更新后的Comment
对象可能看起来像什么,这意味着它可以在GraphQL服务器响应之前乐观地更新UI来显示更新。如果我们的应用程序是错误的(例如,由于错误,GraphQL服务器返回一个未更改的评论),UI会自动更新以反映实际响应。
optimisticResponse
选项
为了启用这种乐观UI行为,我们向执行突变的突变函数提供了optimisticResponse
选项,该函数用于执行我们的突变。
让我们看看一些代码
// Mutation definitionconst UPDATE_COMMENT = gql`mutation UpdateComment($commentId: ID!, $commentContent: String!) {updateComment(commentId: $commentId, content: $commentContent) {id__typenamecontent}}`;// Component definitionfunction CommentPageWithData() {const [mutate] = useMutation(UPDATE_COMMENT);return (<CommentupdateComment={({ commentId, commentContent }) =>mutate({variables: { commentId, commentContent },optimisticResponse: {updateComment: {id: commentId,__typename: "Comment",content: commentContent}}})}/>);}
正如这个示例所示,optimisticResponse
的值是一个与我们期望从服务器接收的突变响应形状匹配的对象。重要的是,这包括Comment
的id
和__typename
字段。Apollo Client缓存使用这些值来生成评论的唯一缓存标识符(例如,Comment:5
)。
乐观突变生命周期
当上面的代码调用
mutate
时,Apollo Client缓存的Comment
对象包含在optimisticResponse
中指定的字段值。然而,它并不用具有相同缓存标识符的现有缓存Comment
覆盖。相反,它存储了一个独立的、乐观版本的该对象。这确保了如果在我们的optimisticResponse
错误的情况下,我们缓存的明数据保持准确。Apollo客户端向所有包含修改评论的活跃查询发出通知。这些查询会自动更新,相关组件也会重新渲染以反映乐观数据。由于这不需要任何网络请求,对用户来说几乎是瞬间的。
最终,我们的服务器响应mutation的实际结果对象。
Apollo客户端缓存会删除乐观版本的
Comment
对象。它还会用服务器返回的值覆盖规范缓存的版本。Apollo客户端再次通知所有受影响的查询。相关组件会重新渲染,但如果服务器响应与我们的
optimisticResponse
相匹配,这样对用户来说就是不可见的。
退出乐观更新自从3.9.0
在某些情况下,您可能希望跳过乐观更新。例如,您可能只想在将某些变量传递给mutation时执行乐观更新。要跳过更新,请向optimisticResponse
选项传递一个函数,并从第二个参数返回IGNORE
哨兵对象来退出乐观更新。
考虑这个例子
const UPDATE_COMMENT = gql`mutation UpdateComment($commentId: ID!, $commentContent: String!) {updateComment(commentId: $commentId, content: $commentContent) {id__typenamecontent}}`;function CommentPageWithData() {const [mutate] = useMutation(UPDATE_COMMENT);return (<CommentupdateComment={({ commentId, commentContent }) =>mutate({variables: { commentId, commentContent },optimisticResponse: (vars, { IGNORE }) => {if (commentContent === "foo") {// conditionally bail out of optimistic updatesreturn IGNORE;}return {updateComment: {id: commentId,__typename: "Comment",content: commentContent}}},})}/>);}
示例:向列表中添加新对象
前面的示例展示了如何为已存在于Apollo客户端缓存的对象提供乐观结果。但创建新对象的mutation呢?这与此类似。
这里最大的区别在于客户端还没有新对象的id
(或其他标识字段)。这意味着您必须提供一个临时值给id
,以便Apollo客户端缓存可以将乐观结果作为正确类型的对象存储。
例如,以下是一个为创建用户待办事项列表中的新项的addTodo
mutation的optimisticResponse
。
optimisticResponse: {addTodo: {id: 'temp-id',__typename: "Todo",description: input.value // Obtained from user input}}
当您在此情况下执行 mutate 函数时,Apollo 客户端缓存将存储一个新的Todo
对象,其缓存标识符为Todo:temp-id
。当服务器响应新的Todo
's 实际 id
,则乐观对象会按常规删除,并将规范对象缓存。
在CodeSandbox上查看
您可以在CodeSandbox上查看完整的待办事项列表示例
您还可以通过克隆docs-examples
仓库来本地运行示例客户端和服务器。
当查看示例时,请尝试向待办事项列表中添加项目。注意,即使在服务器未立即响应的情况下,该项目也会立即显示在列表中。
然后,尝试编辑待办事项列表中的现有项目。注意,该项目不会立即更新。这是因为客户端没有为updateTodo
mutation提供optimisticResponse
。这有助于说明optimisticResponse
提供的改进响应能力。