【发布时间】:2020-06-06 05:47:24
【问题描述】:
对于这个冗长的问题,我深表歉意,但我非常感谢您对 Apollo Client 3 中缓存失效和重新获取查询的最佳策略的一些想法/帮助。
背景
首先,关于我想象的场景的一些信息:
- 有一个
Account组件(如下示例),它使用 react-apollo 中的useQuery挂钩来获取和显示有关帐户的一些基本信息以及该帐户的交易列表 - 在应用程序的其他地方,有一个
CreateTransactionForm组件使用突变插入新事务。这是一个单独的组件,它位于组件树中的不同位置,不一定是AccountComponent的子组件 - 至关重要的是,在服务器上存储事务的过程除了将实际事务插入数据库之外还有一些重要的副作用:
- 所有其他交易发生在之后被插入(按时间顺序)的交易都会更新为新的运行余额
- 任何相关帐户都会更新为新的当前余额
我的Account 组件的简化版本可能如下所示:
import { gql, useQuery } from '@apollo/client';
import React from 'react';
import { useParams } from 'react-router-dom';
const GET_ACCOUNT_WITH_TRANSACTIONS = gql`
query getAccountWithTransactions($accountId: ID!) {
account(accountId: $accountId) {
_id
name
description
currentBalance
transactions {
_id
date
amount
runningBalance
}
}
}
`;
export const Account: React.FunctionComponent = () => {
const { accountId } = useParams();
const { loading, error, data } = useQuery(GET_ACCOUNT_WITH_TRANSACTIONS, {
variables: { accountId },
});
if (loading) { return <p>Loading...</p>; }
if (error) { return <p>Error</p>; }
return (
<div>
<h1>{data.account.name}</h1>
{data.account.transactions.map(transaction => (
<TransactionRow key={transaction._id} transaction={transaction} />
))}
</div>
);
};
潜在策略
我正在评估使 Apollo 客户端缓存的部分无效并在插入事务后重新获取适当数据的各种选项。根据我目前了解到的情况,有一些潜在的策略:
a) 调用useQuery 返回的refetch 方法强制Account 组件重新加载其数据
- 这似乎是可靠的,并且会返回服务器获取新数据,但
CreateTransactionForm需要(直接或间接)耦合到Account组件,因为需要触发对refetch的调用
b) 将查询名称 (getAccountWithTransactions) 传递给突变的 refetchQueries 选项
- 类似于 a,但可能具有更紧密的耦合 -
CreateTransactionForm需要了解应用程序中存在并可能受突变影响的所有其他组件/查询(如果将来添加更多组件/查询) ,这意味着记得更新CreateTransactionForm)
c) 执行突变后手动修改缓存的内容
- 我想这将非常复杂/难以维护,因为
CreateTransactionForm需要确切地知道哪些数据由于服务器的操作而发生了变化。如前所述,这可能不是微不足道的数据量,在执行突变后,我们不仅需要检索有关已插入事务的更新数据,还需要检索任何其他已作为副作用更新的其他数据,以及受影响的帐户,等等。它也可能不是很有效,因为其中一些信息可能永远不会在客户端中再次查看。
我的直觉是,以上选项都不理想。特别是,随着应用程序的增长,我担心可维护性;如果组件需要明确知道哪些其他组件/查询可能会受到对数据图的更改的影响,那么一旦应用程序变得越来越大,就会很容易错过一个并引入细微的错误复杂。
更好的方法?
我对 Apollo Client 3 中引入的新 evict 和 gc 方法非常感兴趣,我想知道它们是否可以提供更简洁的解决方案。
我正在考虑的是,在调用突变后,我可以使用这些新功能:
- 积极驱逐交易中包含的所有账户上的
transactions数组 - 另外,删除所有受影响帐户上的
currentBalance字段
例如:
const { cache } = useApolloClient();
...
// after calling the mutation:
cache.evict(`Account:${accountId}`, 'transactions');
cache.evict(`Account:${accountId}`, 'currentBalance');
cache.gc();
以上提供了一种从缓存中删除陈旧数据的简单方法,并确保组件将在下次执行这些字段查询时进入网络。例如,如果我导航到不同的页面并返回到 Account 页面,这会很有效。
我的主要问题(终于!)
这引出了我不确定的主要难题:
有什么方法可以检测到查询中引用的部分或全部数据已从缓存中清除?
我不确定这对库来说是否可行,但如果可能的话,我认为它可以简化代码并减少应用程序不同部分之间的耦合。
我的想法是,这将使每个组件变得更具“反应性”——组件只知道它们依赖哪些数据,并且每当底层缓存图中的数据丢失时,它可以立即通过触发自身重新获取来做出反应询问。组件最好以声明方式对它们所依赖的数据的变化做出反应,而不是在有意义的情况下强制通信以触发彼此的操作。
【问题讨论】:
-
我通过驱逐字段成功触发了重新获取。我只是希望它有更好的记录。
标签: reactjs caching apollo apollo-client