Mat*_*son 15 caching apollo reactjs apollo-client
对于这个长问题,我深表歉意,但我非常感谢您对 Apollo Client 3 中缓存失效和重新获取查询的最佳策略的一些想法/帮助。
首先,关于我想象的场景的一些信息:
Account
组件(下面的示例)使用useQuery
react-apollo 中的钩子来获取和显示有关帐户的一些基本信息以及该帐户的交易列表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>
);
};
Run Code Online (Sandbox Code Playgroud)
我正在评估使 Apollo 客户端缓存的部分无效并在插入事务后重新获取适当数据的各种选项。从我到目前为止所学到的,有一些潜在的策略:
a) 调用refetch
返回的方法useQuery
强制Account
组件重新加载数据
CreateTransactionForm
需要(直接或间接)耦合到Account
组件,因为需要触发该调用refetch
b) 将查询名称 ( ) 传递getAccountWithTransactions
到refetchQueries
变异选项中
CreateTransactionForm
需要了解应用程序中存在的所有其他组件/查询,并且可能会受到突变的影响(如果将来添加更多,这将意味着记住更新CreateTransactionForm
)c) 执行mutation后手动修改缓存内容
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();
Run Code Online (Sandbox Code Playgroud)
以上提供了一种从缓存中删除陈旧数据的简单方法,并确保下次执行这些字段查询时组件将进入网络。例如,如果我导航到不同的页面并返回到该Account
页面,这很有效。
这导致了我不确定的难题的主要部分:
有什么方法可以检测查询中引用的部分或全部数据是否已从缓存中逐出?
我不确定这是否是库所期望的可行的事情,但如果可能的话,我认为它可能会导致更简单的代码和应用程序不同部分之间的更少耦合。
我的想法是,这将使每个组件变得更具“反应性”——组件只知道它们依赖哪些数据,并且每当底层缓存图中的数据丢失时,它可以通过触发对自己的查询的重新获取来立即做出反应。如果组件对它们所依赖的数据的变化做出声明性的反应,而不是强制地进行通信以触发对彼此的操作,那会很好。
这里有两个答案,执行此操作的标准实践方法是什么?我们怎样才能创造出更好的方法呢?
标准实践
https://www.apollographql.com/blog/when-to-use-refetch-queries-in-apollo-client/
Apollo 在他们的博客和文档中指出,如果您的突变在返回的查询之外产生副作用,那么如果您想保持缓存一致性,则必须使用更新函数。这与 using 类似,refetchQueries
但更明确,因为您自己更新缓存(它也需要更少的网络请求)。尽管如此,您是对的,这种方法将导致交易的突变更新与所有其他依赖组件之间的耦合。
我相信构建它的最佳方法是为所有突变具有副作用的组件定义一个更新函数。然后事务将调用它影响的所有组件更新函数。这些反过来又会调用它们自己的依赖项。这可能会变得混乱,所以让我们看看您是否可以自己进行缓存控制。
更好的方法
我不相信您可以检查 Apollo 缓存是否存在数据,但您可以检查 Redis 缓存是否存在相同的信息,并且 Apollo 允许您插入其他缓存实现。您甚至可以编写自己的缓存实现并使用 Redis 作为您的实现的后端。这将使您能够更好地控制所需的缓存。但是,这需要大量的前期工程成本。
结论:
由于您的副作用似乎在交易和帐户组件(只有两个组件)内,我想说您现在应该编写一个自定义更新函数。一旦遇到更复杂的缓存一致性问题,我会考虑深入研究自定义缓存实现。
归档时间: |
|
查看次数: |
964 次 |
最近记录: |