很抱歉,这个问题很长,但是对于在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 Client缓存部分无效并在插入事务后重新获取适当数据的各种选项。根据到目前为止的经验,有一些潜在的策略:
a)调用refetch
返回的useQuery
方法以强制Account
组件重新加载其数据
CreateTransactionForm
必须(直接或间接)耦合到Account
组件,因为需要触发某些调用refetch
b)将查询名称(getAccountWithTransactions
)传递到突变的refetchQueries
选项中
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
页面,则效果很好。
这导致了我不确定的难题的主要部分:
是否有任何方法可以检测到查询中引用的部分或全部数据已从缓存中清除?
我不确定这对库是否可行,但是如果有可能,我认为这可能会简化代码,并减少应用程序不同部分之间的耦合。
我的想法是,这将使每个组件变得更加“活跃”-组件只需知道它们所依赖的数据,并且只要底层缓存图中的数据丢失,它就可以通过自身触发触发立即做出反应。查询。对于组件来说,最好以声明方式对它们所依赖的数据变化做出反应,而不是在必要时强制性地相互进行触发操作。
答案 0 :(得分:1)
这里有两个答案,执行此操作的标准做法是什么?我们如何才能使这种更好的方式?
标准做法
https://www.apollographql.com/blog/when-to-use-refetch-queries-in-apollo-client/
Apollo 在他们的博客和文档中指出,如果您的变异在返回的查询之外有副作用,那么您必须使用更新功能,如果您想保持缓存一致性。这与使用 refetchQueries
类似,但在您自己更新缓存时更加明确(它还需要更少的网络请求)。不过,您是对的,这种方法会导致您的事务的变更更新与所有其他依赖组件之间发生耦合。
我相信构建它的最佳方法是为所有其突变具有副作用的组件定义一个更新函数。然后事务将调用它影响的所有组件更新函数。这些反过来会调用它们自己的依赖项。这可能会变得混乱,所以让我们看看您是否可以自己进行缓存控制。
更好的方法
我不相信您可以检查 Apollo 缓存是否存在数据,但是您可以检查 Redis 缓存以获取相同的信息,并且 Apollo 允许您插入其他缓存实现。您甚至可以编写自己的缓存实现并使用 Redis 作为您的实现的后端。这将使您可以更好地控制所需的缓存。但是,这需要大量的前期工程成本。
结论:
由于您的副作用似乎在交易和帐户组件(只有两个组件)内,我认为您现在应该编写自定义更新函数。一旦遇到更复杂的缓存一致性问题,我会考虑深入研究自定义缓存实现。