在 Apollo Client 中重新获取查询
Apollo Client允许您通过GraphQL数据本地修改。更新缓存,但有时通过从服务器重新获取查询来更新客户端的GraphQL数据会更直接。
理论上,您可以在客户端更新后重新获取每个活动查询,但实际上,通过更选择性地的重新获取查询,您可以节省时间和网络带宽。InMemoryCache可以帮助您确定哪些活动查询可能在最近的缓存更新中变得无效。
本地缓存更新和重新获取结合使用效果很好:您的应用可以立即显示本地缓存修改的结果,同时也在后台重新获取,以获取来自服务器的最新数据。只有当本地数据和重新获取的数据之间有差异时,UI 才会重新渲染。
重新获取在查询后尤其常见突变,因此mutate 函数接受refetchQueries和onQueryUpdated等选项来指定应重新获取哪些查询以及如何获取。
为了选择性重新获取浆液 外部的突变,您应使用 refetchQueries 方法,该方法属于 ApolloClient,此处有相关文档。
client.refetchQueries
此方法首次出现在 Apollo Client 3.4 中。
重新获取选项
该方法接受一个符合以下 TypeScript 接口的 options 对象,用于指定重新获取浆液的选项:
interface RefetchQueriesOptions<TCache extends ApolloCache<any>,TResult = Promise<ApolloQueryResult<any>>,> {updateCache?: (cache: TCache) => void;include?: Array<string | DocumentNode> | "all" | "active";onQueryUpdated?: (observableQuery: ObservableQuery<any>,diff: Cache.DiffResult<any>,lastDiff: Cache.DiffResult<any> | undefined,) => boolean | TResult;optimistic?: boolean;}
以下列出了这些 字段 的描述:
| 名称 类型 | 描述 |
|---|---|
| 可选函数,用于更新缓存的字段并触发包含这些字段的浆液的重新获取。 |
| 可选数组,指定要重新获取的浆液。每个元素可以是浆液的字符串名称或 类似于用于突变的 使用 |
| 可选的回调函数,对每个受 如果未提供 从 |
| 当 默认为 |
重新获取结果
客户端的 refetchQueries 方法收集由 onQueryUpdated 返回的 TResult 结果,默认为 TResult = Promise<ApolloQueryResult<any>> 如果未提供 onQueryUpdated。它使用 Promise.all(results) 将这些结果合并为一个单一的 Promise<TResolved[]>。
由于 Promise.all 的 Promise - 解包行为,TResolved 类型通常与 TResult 相同,除非 TResult 是 PromiseLike<TResolved> 或 boolean。
返回的 Promise 对象具有另外两个有用的属性:
| 名称 类型 | 描述 |
|---|---|
| 一个被重新获取的 |
| 一个结果数组,这些结果是通过 如果 如果 |
这两个数组相互平行:它们的长度相同,并且 results[i] 是当使用位于 queries[i] 的 ObservableQuery 时,由 onQueryUpdated 产生的结果,对于任何索引 i。
重新获取配方
重新获取特定查询
要通过名称重新获取特定的 query,请使用仅包含 include 选项:
await client.refetchQueries({include: ["SomeQueryName"],});
选项 include 还可以使用其 DocumentNode 重新获取特定的 query:
await client.refetchQueries({include: [SOME_QUERY],});
重新获取所有查询
要重新获取所有 活动 查询,请传递 "active" 的include 简写:
await client.refetchQueries({include: "active",});
为了重新获取 所有 由 Apollo 客户端管理的查询(即使是没有观察者或当前未挂载的组件的查询),请将 "all" 传递给 include:
await client.refetchQueries({include: "all", // Consider using "active" instead!});
重新获取受缓存更新影响的查询
您可以通过 updateCache 回调来重新获取受缓存更新影响的查询:
await client.refetchQueries({updateCache(cache) {cache.evict({ fieldName: "someRootField" });},});
这会重新获取任何依赖于 Query.someRootField 的查询,而无需您事先知道哪些查询可能包含在内。允许在 updateCache 中的 缓存操作(writeQuery、writeFragment、modify、evict 等):
默认情况下,updateCache 在缓存中持久更新。如果您想在 client.refetchQueries Done 观察它们后立即丢弃它们,而不是更改缓存,您可以在临时的乐观层中执行它们:
await client.refetchQueries({updateCache(cache) {cache.evict({ fieldName: "someRootField" });},// Evict Query.someRootField only temporarily, in an optimistic layer.optimistic: true,});
另一种在不实际更改缓存数据的情况下更新缓存的方法是使用 cache.modify 和其 INVALIDATE 信号对象:
await client.refetchQueries({updateCache(cache) {cache.modify({fields: {someRootField(value, { INVALIDATE }) {// Update queries that involve Query.someRootField, without actually// changing its value in the cache.return INVALIDATE;},},});},});
在引入 client.refetchQueries 之前,INVALIDATE 信号无效,因为具有 fetchPolicy: "cache-first" 的无效查询通常会重新读取未更改的结果,因此决定不执行网络请求。client.refetchQueries 方法使此无效化系统更容易为应用程序代码访问,因此您可以控制无效查询的重新获取行为。
在上面的所有示例中,无论我们使用 include 还是 updateCache,client.refetchQueries 都会从网络上重新获取受影响的查询,并将 Promise<ApolloQueryResult<any>> 结果包括在 Promise<TResolved[]> 返回的 client.refetchQueries。
如果特定的 查询 同时被 include 和 updateCache 包含,则该查询只会重新获取一次。换句话说,include 选项是一个确保某些查询始终包括在内的好方法,无论哪个查询被 updateCache 包含。
有选择地重新获取
在开发过程中,您可能想要确保适当的查询被重新获取,而不是盲目地重新获取它们。为了在重新获取之前拦截每个 查询,您可以指定一个 onQueryUpdated 回调:
const results = await client.refetchQueries({updateCache(cache) {cache.evict({ fieldName: "someRootField" });},onQueryUpdated(observableQuery) {// Logging and/or debugger breakpoints can be useful in development to// understand what client.refetchQueries is doing.console.log(`Examining ObservableQuery ${observableQuery.queryName}`);debugger;// Proceed with the default refetching behavior, as if onQueryUpdated// was not provided.return true;},});results.forEach(result => {// These results will be ApolloQueryResult<any> objects, after all// results have been refetched from the network.});
注意在本例中添加onQueryUpdated并没有改变client.refetchQueries的重新获取行为,这使得我们可以纯粹用于诊断或调试目的。
如果您想跳过某些其他情况下会被包含的查询,请从onQueryUpdated返回false:
await client.refetchQueries({updateCache(cache) {cache.evict({ fieldName: "someRootField" });},onQueryUpdated(observableQuery, { complete, result, missing }) {console.log(`Examining ObservableQuery ${observableQuery.queryName} whose latest result is ${JSON.stringify(result)} which is ${complete ? "complete" : "incomplete"}`);if (shouldIgnoreQuery(observableQuery)) {return false;}// Refetch the query unconditionally from the network.return true;},});
如果ObservableQuery提供的信息不足,您也可以检查查询的最新result,以及有关其complete性和缺失字段的DiffResult对象,该对象作为onQueryUpdated的第二个参数传入:
await client.refetchQueries({updateCache(cache) {cache.evict({ fieldName: "someRootField" });},onQueryUpdated(observableQuery, { complete, result, missing }) {if (shouldIgnoreQuery(observableQuery)) {return false;}if (complete) {// Update the query according to its chosen FetchPolicy, rather than// refetching it unconditionally from the network.return observableQuery.reobserve();}// Refetch the query unconditionally from the network.return true;},});
由于onQueryUpdated具有动态过滤查询的能力,因此它与上面提到的批量include选项也很好地配合使用:
await client.refetchQueries({// Include all active queries by default, which may be ill-advised unless// you also use onQueryUpdated to filter those queries.include: "active";// Called once for every active query, allowing dynamic filtering:onQueryUpdated(observableQuery) {return !shouldIngoreQuery(observableQuery);},});
处理重取错误
在上面的例子中,我们使用await client.refetchQueries(...)来获取所有重取查询的最终ApolloQueryResult<any>结果。这个合并的Promise.all被创建,所以一个失败会拒绝整个Promise<TResolved[]>,可能隐藏其他成功的结果。如果这是一个问题,您可以使用client.refetchQueries返回的queries和results数组来代替(或与)await:
const { queries, results } = client.refetchQueries({// Specific client.refetchQueries options are not relevant to this example.});const finalResults = await Promise.all(results.map((result, i) => {return Promise.resolve(result).catch(error => {console.error(`Error refetching query ${queries[i].queryName}: ${error}`);return null; // Silence this Promise rejection.});})});
在未来,正像可能会向client.refetchQueries方法添加额外的输入选项一样,它的结果对象可能会添加更多属性,补充其Promise相关属性以及queries和results数组。
如果您发现某些特定的额外client.refetchQueries输入选项或结果属性可能会有所帮助,请随时在这里打开一个问题
对应的 client.mutate 选项
在client.mutate支持与选项类似,您应该使用它而不是client.refetchQueries,因为重取逻辑在mutation的过程中的特定时间点是重要的。
由于历史原因, client.mutate 选项的名称与新的 client.refetchQueries 选项略有不同,但它们的内部实现基本上相同,因此您可以使用以下表格在它们之间进行转换:
client.mutate(options) | client.refetchQueries(options) | |
|---|---|---|
options.refetchQueries | ⇔ | options.include |
options.update | ⇔ | options.updateCache |
options.onQueryUpdated | ⇔ | options.onQueryUpdated |
options.awaitRefetchQueries | ⇔ | 从 onQueryUpdated 返回一个 Promise |