在 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 |