加入我们,于10月8日至10日在纽约市,学习关于GraphQL联邦和API平台工程的最新技巧、趋势和新闻。在纽约市参加2024年GraphQL峰会
文档
免费开始

乐观突变结果

在服务器响应之前更新你的 UI


通常可以预测 的最有可能的结果 你的 返回之前。 可以使用这个“最有可能的结果”以乐观的方式更新你的 UI ,让你的应用对用户更具有响应性。

例如,假设我们有一个支持以下 mutation 的博客应用:

type Mutation {
updateComment(commentId: ID!, content: String!): Comment!
# ...other mutations...
}

如果用户编辑了博客上的现有评论,应用程序会执行updateComment 突变操作,它会返回一个更新了内容Comment对象。

我们知道更新后的Comment对象可能看起来像什么,这意味着它可以在GraphQL服务器响应之前乐观地更新UI来显示更新。如果我们的应用程序是错误的(例如,由于错误,GraphQL服务器返回一个未更改的评论),UI会自动更新以反映实际响应。

optimisticResponse 选项

为了启用这种乐观UI行为,我们向执行突变的突变函数提供了optimisticResponse选项,该函数用于执行我们的突变。

让我们看看一些代码

CommentPageWithData.jsx
// Mutation definition
const UPDATE_COMMENT = gql`
mutation UpdateComment($commentId: ID!, $commentContent: String!) {
updateComment(commentId: $commentId, content: $commentContent) {
id
__typename
content
}
}
`;
// Component definition
function CommentPageWithData() {
const [mutate] = useMutation(UPDATE_COMMENT);
return (
<Comment
updateComment={({ commentId, commentContent }) =>
mutate({
variables: { commentId, commentContent },
optimisticResponse: {
updateComment: {
id: commentId,
__typename: "Comment",
content: commentContent
}
}
})
}
/>
);
}

正如这个示例所示,optimisticResponse的值是一个与我们期望从服务器接收的突变响应形状匹配的对象。重要的是,这包括Commentid__typename字段。Apollo Client缓存使用这些值来生成评论的唯一缓存标识符(例如,Comment:5)。

乐观突变生命周期

  1. 当上面的代码调用mutate时,Apollo Client缓存的Comment对象包含在optimisticResponse中指定的字段值。然而,它并不用具有相同缓存标识符的现有缓存Comment覆盖。相反,它存储了一个独立的、乐观版本的该对象。这确保了如果在我们的optimisticResponse错误的情况下,我们缓存的明数据保持准确。

  2. Apollo客户端向所有包含修改评论的活跃查询发出通知。这些查询会自动更新,相关组件也会重新渲染以反映乐观数据。由于这不需要任何网络请求,对用户来说几乎是瞬间的。

  3. 最终,我们的服务器响应mutation的实际结果对象。

  4. Apollo客户端缓存会删除乐观版本的Comment对象。它还会用服务器返回的值覆盖规范缓存的版本。

  5. Apollo客户端再次通知所有受影响的查询。相关组件会重新渲染,但如果服务器响应与我们的optimisticResponse相匹配,这样对用户来说就是不可见的。

退出乐观更新
自从3.9.0

在某些情况下,您可能希望跳过乐观更新。例如,您可能只想在将某些传递给mutation时执行乐观更新。要跳过更新,请向optimisticResponse选项传递一个函数,并从第二个返回IGNORE哨兵对象来退出乐观更新。

考虑这个例子

const UPDATE_COMMENT = gql`
mutation UpdateComment($commentId: ID!, $commentContent: String!) {
updateComment(commentId: $commentId, content: $commentContent) {
id
__typename
content
}
}
`;
function CommentPageWithData() {
const [mutate] = useMutation(UPDATE_COMMENT);
return (
<Comment
updateComment={({ commentId, commentContent }) =>
mutate({
variables: { commentId, commentContent },
optimisticResponse: (vars, { IGNORE }) => {
if (commentContent === "foo") {
// conditionally bail out of optimistic updates
return 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上查看完整的待办事项列表示例

Edit todo-list-client

您还可以通过克隆docs-examples 仓库来本地运行示例客户端和服务器。

当查看示例时,请尝试向待办事项列表中添加项目。注意,即使在服务器未立即响应的情况下,该项目也会立即显示在列表中。

然后,尝试编辑待办事项列表中的现有项目。注意,该项目不会立即更新。这是因为客户端没有为updateTodo mutation提供optimisticResponse。这有助于说明optimisticResponse提供的改进响应能力。

上一页
提升性能
下一页
服务器端渲染
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,也称为Apollo GraphQL。

隐私政策

公司