最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to update Apollo cache after query? - Stack Overflow

programmeradmin2浏览0评论

How can I override a value in the Apollo cache?

I have a graphql query to fetch the user. This returns a user with a default currency. This currency can then be override from a select dropdown.

The query fetches paymentCurrencies from an API, then uses a client side resolver to set the first item in the array of paymentCurrencies to be the users currency

query me {
  me {
    username
    currency @client
    paymentCurrencies
  }
}

When somebody selects a currency from the dropdown menu, I want to over the users currency with whatever they have selected.

I have something like this so far:

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

I get the following error: 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

Is using writeData is the correct method or should I be using writeQuery or something?

How can I override a value in the Apollo cache?

I have a graphql query to fetch the user. This returns a user with a default currency. This currency can then be override from a select dropdown.

The query fetches paymentCurrencies from an API, then uses a client side resolver to set the first item in the array of paymentCurrencies to be the users currency

query me {
  me {
    username
    currency @client
    paymentCurrencies
  }
}

When somebody selects a currency from the dropdown menu, I want to over the users currency with whatever they have selected.

I have something like this so far:

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

I get the following error: 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

Is using writeData is the correct method or should I be using writeQuery or something?

Share Improve this question asked Jan 25, 2020 at 14:01 Stretch0Stretch0 9,31315 gold badges94 silver badges159 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 1

As described in the other answer you probably want a simple query and mutation setup. The client directive is used to extend your schema to hold client-only additional data. From your explanation, it sounds like you explicitly want this data to be syncronised with the server.

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>
    </>
  );
}

You get the idea. Apollo will now automatically update your cache. Make sure your mutation allows to query the updated user object.

For the automatic update to work your user needs to be recognised by the cache. You can do that by either adding an id field and selecting it in both the query and the mutation or implementing a dataIdFromObject function in Apollo Client 2.x that includes the username for __typename === 'User' or by using a type policy in Apollo Client 3.x. Find the docs here.

writeData should be used for changing fields at the root, for example:

{
  yourState @client
}

In this case, you should use writeQuery. Additionally, this logic should really be extracted into a (local) mutation you can then call inside your ponent. When using writeQuery, the basic idea is to grab the existing data, make a copy and then transform it as needed:

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

You can also use writeFragment to directly modify a single instance of an object in the cache. However, you need the object's cache key for this. Because the cache key is derived from the __typename and an id field, you should make sure the query includes an id field first. This is good practice regardless to ensure your cache can be updated easily (see here for more details). Then you can do something like this:

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

It depends.

For persistent change (sync to server) you should just change user setting using mutation.

For session aware change - don't use user settings - copy this (from logged user properties) to separate value in global app state (redux/mobx or as in this case apollo local state).

In both cases rare problem can be with updating many ponents using changed data.

Redux/mobx solves this automatically.
Apollo HOCs won't rerender.
Hooks - updates partially (only the one with useQuery and useMutation pair), others will be updated only when rerendered.
Components build with <Query/> creates internally an observable then they will be updated, too.

Mutation have update and refetchQueries parameters for. There is also a package for more plex use cases.

发布评论

评论列表(0)

  1. 暂无评论