查询后如何更新 Apollo 缓存?

Posted

技术标签:

【中文标题】查询后如何更新 Apollo 缓存?【英文标题】:How to update Apollo cache after query? 【发布时间】:2020-05-11 13:52:02 【问题描述】:

如何覆盖 Apollo 缓存中的值?

我有一个 graphql 查询来获取用户。这将返回具有默认货币的用户。然后可以从选择下拉列表中覆盖该货币。

查询从 API 获取 paymentCurrencies,然后使用客户端解析器将 paymentCurrencies 数组中的第一项设置为用户 currency

query me 
  me 
    username
    currency @client
    paymentCurrencies
  

当有人从下拉菜单中选择一种货币时,我想用他们选择的任何货币覆盖用户的货币。

到目前为止,我有这样的事情:

const onChange = e => 
  const  value  = e.target
  client.writeData( user:  currency: value, username, __typename: "User"  )

我收到以下错误:Error writing result to store for query: "kind":"Document","definitions":["kind":"OperationDefinition","operation":"query","name":"kind":"Name","value":"GeneratedClientQuery","selectionSet":null] Cannot read property 'selections' of null

使用writeData 是正确的方法还是应该使用writeQuery 之类的?

【问题讨论】:

【参考方案1】:

如另一个答案中所述,您可能需要一个简单的查询和变异设置。 client 指令用于扩展您的模式以保存仅限客户端的附加数据。根据您的解释,听起来您明确希望将此数据与服务器同步。

const ME_QUERY = gql`
  query me 
    me 
      username
      currency
      paymentCurrencies
    
  
`;

const CURRENCY_MUTATION = gql`
  mutation setCurrency($currency: String) 
    setCurrency(currency: $currency) 
      me 
        username
        currency
      
    
  
`;

function MyComponent() 
  const  data  = useQuery(ME_QUERY);
  const [setCurrency] = useMutation(CURRENCY_MUTATION);

  const onChange = e => setCurrency(
    variables:  currency: e.currentTarget.value ,
  );

  return (
    <>
      <h2>data && data.me.currency</h2>
      <select onChange=onChange>
        /* your dropdown logic */
      </select>
    </>
  );

你明白了。 Apollo 现在将自动更新您的缓存。确保您的变异允许查询更新的用户对象。

为了使自动更新工作您的用户需要被缓存识别。您可以通过添加一个 id 字段并在查询和突变中选择它,或者在 Apollo Client 2.x 中实现一个 dataIdFromObject 函数,其中包括 __typename === 'User' 的用户名,或者在 Apollo Client 中使用类型策略来做到这一点3.x。找到docs here。

【讨论】:

那么 setCurrency 会是客户端解析器突变吗?我仍然需要将货币写入缓存的解析器中的逻辑,但不是吗?【参考方案2】:

writeData 应该用于更改根的字段,例如:


  yourState @client

在这种情况下,您应该使用 writeQuery。此外,这个逻辑真的应该被提取到一个(本地)突变中,然后您可以在组件内部调用。使用writeQuery时,基本思路是抓取已有数据,进行复制,然后根据需要进行转换:

const  me  = client.readQuery( query: ME_QUERY )
const data = 
  me: 
    ...me,
    currency: value,
  

client.writeQuery( query: ME_QUERY, data )

您也可以使用writeFragment 直接修改缓存中对象的单个实例。但是,您需要对象的缓存键。因为缓存键派生自 __typenameid 字段,所以您应该确保查询首先包含 id 字段。无论确保您的缓存可以轻松更新,这是一个很好的做法(有关更多详细信息,请参阅here)。然后你可以这样做:

client.writeFragment(
  id: 'User:42',
  fragment: gql`
    fragment UserCurrency on User 
      currency @client
    
  `,
  data: 
    currency: value,
  ,
)

【讨论】:

【参考方案3】:

视情况而定。

对于持久更改(同步到服务器),您应该只使用突变更改用户设置。

对于会话感知更改 - 不要使用用户设置 - 将此(从记录的用户属性)复制到全局应用状态(redux/mobx 或在本例中为 apollo local state 的 separate 值) )。

在这两种情况下,使用更改的数据更新许多组件可能会出现罕见的问题。

Redux/mobx 自动解决了这个问题。 Apollo HOC 不会重新渲染。 钩子 - 部分更新(仅具有 useQueryuseMutation 对的一个),其他将仅在重新渲染时更新。 使用 &lt;Query/&gt; 构建的组件会在内部创建一个 observable,然后它们也会被更新。

Mutation 有updaterefetchQueries 参数。 还有一个package 用于更复杂的用例。

【讨论】:

以上是关于查询后如何更新 Apollo 缓存?的主要内容,如果未能解决你的问题,请参考以下文章

在 Apollo 客户端上更新缓存

如何从突变查询中更新 Apollo 数据存储/缓存,更新选项似乎没有触发

如何在 Apollo / GraphQL 发生突变后更新缓存?

React Apollo 在突变后更新客户端缓存

为啥我的 Apollo 缓存更新没有反映在我的查询中?

如何使用大量活动查询更新 Apollo Cache