Apollo 客户端:在创建过程中进行乐观更新
Apollo client: Making optimistic updates while creation is still in progress
我希望能够在对象仍在创建时对其进行更新。
例如:假设我有一个待办事项列表,我可以在其中添加带有名称的项目。我还希望能够编辑项目的名称。
现在假设连接速度较慢的用户创建了一个项目。在这种情况下,我会触发创建项目突变并乐观地更新我的 UI。效果很好。目前没问题
现在假设由于网络速度慢,创建项目变更需要一些时间。那时,用户决定编辑他们刚刚创建的项目的名称。为了获得理想的体验:
- UI 应立即更新为新名称
- 新名称最终应该保存在服务器中
我可以通过等待创建突变完成(以便我可以获得项目 ID),然后进行更新名称突变来实现 #2。但这意味着我的 UI 的部分内容将保持不变,直到创建项目突变 returns 和更新名称突变的乐观响应开始。这意味着 #1 将无法实现。
所以我想知道如何使用 Apollo 客户端同时实现#1 和#2。
注意:我不想添加微调器或禁用编辑。我希望应用即使在连接速度较慢的情况下也能响应。
如果您有权访问服务器,您可以执行 upsert
操作,并且可以将所有查询减少到这样一个:
mutation {
upsertTodoItem(
where: {
key: $itemKey # Some unique key generated on client
}
update: {
listId: $listId
text: $itemText
}
create: {
key: $itemKey
listId: $listId
text: $itemText
}
) {
id
key
}
}
因此您将拥有一系列相同的突变,仅在变量上有所不同。相应地,可以针对这一突变配置乐观的响应。在服务器上,您需要检查是否已经存在带有 key
的项目,并分别创建或更新项目。
此外,您可能希望使用 apollo-link-debounce 来减少用户输入时的请求数量。
我认为实现预期效果的最简单方法是实际放弃乐观更新,转而自行管理组件状态。我现在没有足够的带宽写出一个完整的例子,但是你的基本组件结构应该是这样的:
<ApolloConsumer>
{(client) => (
<Mutation mutation={CREATE_MUTATION}>
{(create) => (
<Mutation mutation={EDIT_MUTATION}>
{(edit) => (
<Form />
)}
</Mutation>
)}
</Mutation>
)}
</ApolloConsumer>
假设我们只处理一个字段 -- name
。您的 Form
组件将以
的初始状态开始
{ name: '', created: null, updates: null }
提交后,表单会执行以下操作:
onCreate () {
this.props.create({ variables: { name: this.state.name } })
.then(({ data, errors }) => {
// handle errors whichever way
this.setState({ created: data.created })
if (this.state.updates) {
const id = data.created.id
this.props.update({ variables: { ...this.state.updates, id } })
}
})
.catch(errorHandler)
}
然后编辑逻辑看起来像这样:
onEdit () {
if (this.state.created) {
const id = this.state.created.id
this.props.update({ variables: { name: this.state.name, id } })
.then(({ data, errors }) => {
this.setState({ updates: null })
})
.catch(errorHandler)
} else {
this.setState({ updates: { name: this.state.name } })
}
}
实际上,您的编辑变更要么在用户提交时立即触发(因为我们已经从我们的创建变更中收到响应)...或者用户所做的更改被持久化,然后在创建变更后发送完成。
这是一个非常粗略的示例,但应该可以让您了解如何处理此类情况。最大的缺点是您的组件状态有可能与缓存不同步——您需要确保正确处理错误以防止这种情况发生。
这也意味着如果您想将此表单用于 只是 编辑,您需要从缓存中获取数据,然后使用它来填充您的初始状态(即上例中的 this.state.created
)。您可以为此使用 Query
组件,只需确保在获得 Query
组件提供的 data
道具之前不要渲染实际的 Form
组件。
我希望能够在对象仍在创建时对其进行更新。
例如:假设我有一个待办事项列表,我可以在其中添加带有名称的项目。我还希望能够编辑项目的名称。
现在假设连接速度较慢的用户创建了一个项目。在这种情况下,我会触发创建项目突变并乐观地更新我的 UI。效果很好。目前没问题
现在假设由于网络速度慢,创建项目变更需要一些时间。那时,用户决定编辑他们刚刚创建的项目的名称。为了获得理想的体验:
- UI 应立即更新为新名称
- 新名称最终应该保存在服务器中
我可以通过等待创建突变完成(以便我可以获得项目 ID),然后进行更新名称突变来实现 #2。但这意味着我的 UI 的部分内容将保持不变,直到创建项目突变 returns 和更新名称突变的乐观响应开始。这意味着 #1 将无法实现。
所以我想知道如何使用 Apollo 客户端同时实现#1 和#2。
注意:我不想添加微调器或禁用编辑。我希望应用即使在连接速度较慢的情况下也能响应。
如果您有权访问服务器,您可以执行 upsert
操作,并且可以将所有查询减少到这样一个:
mutation {
upsertTodoItem(
where: {
key: $itemKey # Some unique key generated on client
}
update: {
listId: $listId
text: $itemText
}
create: {
key: $itemKey
listId: $listId
text: $itemText
}
) {
id
key
}
}
因此您将拥有一系列相同的突变,仅在变量上有所不同。相应地,可以针对这一突变配置乐观的响应。在服务器上,您需要检查是否已经存在带有 key
的项目,并分别创建或更新项目。
此外,您可能希望使用 apollo-link-debounce 来减少用户输入时的请求数量。
我认为实现预期效果的最简单方法是实际放弃乐观更新,转而自行管理组件状态。我现在没有足够的带宽写出一个完整的例子,但是你的基本组件结构应该是这样的:
<ApolloConsumer>
{(client) => (
<Mutation mutation={CREATE_MUTATION}>
{(create) => (
<Mutation mutation={EDIT_MUTATION}>
{(edit) => (
<Form />
)}
</Mutation>
)}
</Mutation>
)}
</ApolloConsumer>
假设我们只处理一个字段 -- name
。您的 Form
组件将以
{ name: '', created: null, updates: null }
提交后,表单会执行以下操作:
onCreate () {
this.props.create({ variables: { name: this.state.name } })
.then(({ data, errors }) => {
// handle errors whichever way
this.setState({ created: data.created })
if (this.state.updates) {
const id = data.created.id
this.props.update({ variables: { ...this.state.updates, id } })
}
})
.catch(errorHandler)
}
然后编辑逻辑看起来像这样:
onEdit () {
if (this.state.created) {
const id = this.state.created.id
this.props.update({ variables: { name: this.state.name, id } })
.then(({ data, errors }) => {
this.setState({ updates: null })
})
.catch(errorHandler)
} else {
this.setState({ updates: { name: this.state.name } })
}
}
实际上,您的编辑变更要么在用户提交时立即触发(因为我们已经从我们的创建变更中收到响应)...或者用户所做的更改被持久化,然后在创建变更后发送完成。
这是一个非常粗略的示例,但应该可以让您了解如何处理此类情况。最大的缺点是您的组件状态有可能与缓存不同步——您需要确保正确处理错误以防止这种情况发生。
这也意味着如果您想将此表单用于 只是 编辑,您需要从缓存中获取数据,然后使用它来填充您的初始状态(即上例中的 this.state.created
)。您可以为此使用 Query
组件,只需确保在获得 Query
组件提供的 data
道具之前不要渲染实际的 Form
组件。