更新 Apollo Cache 后,您的用户界面将立即受益。
我非常喜欢 Apollo 团队开发的产品,尤其是Apollo Client。它最初只是一个简单的工具,用于连接前端数据需求和 GraphQL 服务器,但如今,它已发展成为一套完整的客户端数据管理解决方案。尽管使用 GraphQL可以减少对本地状态的需求,但 Apollo 仍然提供了一个类似 Redux 的“全局”存储,您可以通过 GraphQL 接口访问它。如果您想了解更多关于如何使用Apollo 实现客户端状态的信息,请点击此处阅读。
我不想讨论本地解析器或仅限前端的状态,而是想谈谈 Apollo 从服务器获取 GraphQL 数据时使用的底层缓存,特别是如何利用它来提升UI 的响应速度和智能化程度。Apollo 官方文档中关于如何与缓存交互的内容很不错,但我觉得它离实际应用场景还有一段距离。
简单的 CRUD
我将创建一个简单的愿望清单 React 应用(虽然大部分内容也适用于其他框架),它只是一个简单的物品 { title: string, price: int }CRUD 操作。像大多数实际应用一样,它包含一个物品列表、一个删除按钮、一个编辑表单和一个添加表单。我主要想展示的是,如何在列表发生变更后更新列表,而无需从服务器重新获取列表。
我将使用我用Codesandbox创建的这个简单服务器(这帮家伙真是太棒了!),唯一需要注意的是,在编辑mutation 中,你的服务器应该返回更新后的资源,稍后你会明白原因。我会添加一些模拟数据,以便我们立即查看结果。
我假设您已经知道如何搭建一个基于 Apollo Client 和 React 的 Apollo 应用。如果还不清楚,请点击这里查看。
所以,我将逐字逐句地讲解CRUD(出于教育目的,记为 RUCD)以及您可能需要使用的功能。
读
const GET_ITEMS = gql`
{
items {
id
title
price
}
}
`;
function Wishlist() {
const { loading, error, data } = useQuery(GET_ITEMS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return data.items.map(item => {
const { id, title, price } = item;
return (
<div key={id}>
{title} - for ${price} -{" "}
</div>
);
});
}
就是这样!我们的读取查询非常简单:它不接受搜索参数、分页元数据,也不使用游标。为了演示方便,这样写比较好,但如果您需要更复杂情况的帮助,我稍后会提供补充说明。仅凭这个配置,我们的列表就已经准备好在后续写入时响应缓存变化了。
注意:使用 `<string> useQuery` 或<Query>`<string>`withQuery在这里至关重要,因为它将数据与缓存关联起来。如果您使用其他解决方案,例如 `<string>`useApolloClient和 `<string>`,那么client.query这些解决方案将无法正常工作。
编辑
我们将创建编辑组件,这是最简单的例子,因为编辑项目后列表会自动更新,无需任何额外操作__typename。Apollo Cache 默认使用键值对(+id或_id)作为每个缓存对象的主键。每次服务器返回数据时,Apollo 都会检查新数据是否替换了缓存中已有的数据。因此,如果您的editItemmutation 返回了完整的更新Item对象,Apollo 会发现缓存中已经存在该项目(来自您获取列表时),更新缓存,并触发useQuery更新data,最终实现Wishlist重新渲染。
const EDIT_ITEM = gql`
mutation($id: Int, $item: ItemInput) {
editItem(id: $id, item: $item) {
id
title
price
}
}
`;
const EditItem = ({ item: { title, price, id } }) => {
const [editItem, { loading }] = useMutation(EDIT_ITEM);
return (
<ItemForm
disabled={loading}
initialPrice={price}
initialTitle={title}
onSubmit={item => {
editItem({
variables: {
id,
item
}
});
}}
/>
);
};
完成!当用户点击Submit编辑表单上的按钮时,变更将被触发,Apollo 客户端将收到更新后的数据,并且该项将在列表中自动更新。
创造
让我们来创建AddItem组件。这次有点不同,因为当我们从服务器收到响应(新创建的列表Item)时,Apollo 并不知道列表是否应该更新(有时也确实不应该更新)。为此,我们需要以编程方式将新项添加到列表中,而钩子函数的一个参数useMutation就是update专门用于此目的的函数。
更新缓存所需的步骤如下:
- 从 Apollo 缓存中读取数据(我们将使用相同的
GET_ITEMS查询语句) - 更新推送我们新产品的商品列表
- 将数据写回 Apollo 缓存(也指查询
GET_ITEMS)
之后,Apollo Client 会注意到该查询的缓存已更改,并且最终还会更新我们的愿望清单。
const ADD_ITEM = gql
mutation($item: ItemInput) {
addItem(item: $item) {
id
title
price
}
}
;
const AddItem = () => {
const [addItem, { loading }] = useMutation(ADD_ITEM);
return (
<ItemForm
disabled={loading}
onSubmit={item => {
addItem({
variables: {
item
},
update: (cache, { data: { addItem } }) => {
const data = cache.readQuery({ query: GET_ITEMS });
data.items = [...data.items, addItem];
cache.writeQuery({ query: GET_ITEMS }, data);
}
});
}}
/>
);
};
删除
删除操作与创建操作非常相似。为了简化操作,我将把它放在愿望清单组件中,并添加一些属性/状态以增强应用程序的整体功能。
const DELETE_ITEM = gql
mutation($id: Int) {
deleteItem(id: $id) {
id
title
price
}
}
;
function Wishlist({ onEdit }) {
const { loading, error, data } = useQuery(GET_ITEMS);
const [deleteItem] = useMutation(DELETE_ITEM);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return data.items.map(item => {
const { id, title, price } = item;
return (
<div key={id}>
{title} - for ${price} -{" "}
<button
className="dim pointer mr2"
onClick={e => {
onEdit(item);
}}
>
edit
</button>
<button
className="dim pointer"
onClick={() => {
deleteItem({ variables: { id },
update: cache => {
const data = cache.readQuery({ query: GET_ITEMS });
data.items = data.items.filter(({id: itemId}) => itemId !== id);
cache.writeQuery({ query: GET_ITEMS }, data);
}});
}}
>
delete
</button>
</div>
);
});
}
高级病例
我们的应用程序中经常会遇到更复杂的列表,例如带有搜索、分页和排序功能的列表。对于这类列表,处理起来会比较复杂,而且很大程度上取决于应用程序的上下文。例如,当删除第四页列表中的一个项目时,我们应该直接从用户界面中删除该项目并显示pageLength - 1剩余的项目,还是应该从下一页获取一个项目并添加进去?
这些情况也很棘手,因为如果提供的查询接收变量,cache.readQuery则还需要这些变量,而这些变量可能无法全局访问。一种方法是使用类似原始查询或Apollo 中返回的变量,但这会导致查询再次访问服务器。如果您想深入了解这个问题,此 issue提供了许多解决方案,特别是用于获取查询上次使用的变量的代码片段。queryrefetchrefetchQueries
最终解决方案
这个应用有一些状态/UI方面的问题,我做了一个最小版本来演示Apollo缓存更新 :)
整个应用程序的开发都在这个 CodeSandbox上。您可能需要 fork这个容器并更新服务器 URL。

欢迎随时联系我 :)
文章来源:https://dev.to/lucis/update-apollo-cache-after-a-mutation-and-get-instant-benefits-on-your-ui-1c3b
