apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.32k stars 2.65k forks source link

useMutation updates cache but does not re-render components querying same object #5963

Open kkitay opened 4 years ago

kkitay commented 4 years ago

Intended outcome:

Actual outcome:

Some screenshots w/ info redacted. Object A is listing and Object B (the object being created) is latestValidPropertyInspection. The initial query:

Screen Shot 2020-02-18 at 5 39 42 PM

The mutation running and returning the new objects w/ non-null latestValidPropertyInspection:

Screen Shot 2020-02-18 at 5 39 57 PM

apollo chrome extension showing the cache updated after the mutation ran:

Screen Shot 2020-02-18 at 5 40 22 PM

query:

  const result = useQuery({
    query: listingById,
    options: { variables: { id: currentListing.id } },
  });

mutation:

const [requestInspection, result] = useMutation(mutation: requestInspectionMutation);

How to reproduce the issue: Query for a single object in one component, with a field being another object but that is returning null. In another component, mutate to create said object, but return the parent (the original single object). You should find that the component did not render.

Versions


  System:
    OS: Linux 4.19 Debian GNU/Linux 8 (jessie) 8 (jessie)
  Binaries:
    Node: 10.11.0 - /usr/local/bin/node
    Yarn: 1.9.4 - /usr/local/bin/yarn
    npm: 6.4.1 - /usr/local/bin/npm
  npmPackages:
    @apollo/react-hooks: ^3.1.3 => 3.1.3
    apollo-cache-inmemory: ^1.6.3 => 1.6.3
    apollo-client: ^2.6.4 => 2.6.4
    apollo-link: ^1.2.13 => 1.2.13
    apollo-link-error: ^1.1.12 => 1.1.12
    apollo-link-retry: ^2.2.15 => 2.2.15
    apollo-link-state: ^0.4.2 => 0.4.2
    apollo-upload-client: ^11.0.0 => 11.0.0
    react-apollo: ^3.1.3 => 3.1.3
darshan0919 commented 1 year ago

@alessbell, I agree with @chrisregner and @RemyMachado . can you confirm if we are on the right track?

phryneas commented 11 months ago

I'd say @chrisregner is on the right track:

A component will update if a selected field has been updated in the cache.

That means a few prerequisites:

mitidiero commented 7 months ago

I am seeing the same issue as @RemyMachado reported in the version 3.8.9 however, it seemed fixed in the version 3.7.17.

In my case I have a list that allow users to perform an update in an entity that is loaded via a network request. I am using the cache-first as the fetch policy. Whenever the user tries to update the entity of a record, they can also add a new record to it. This new record shows up in the other instances of this list only if I refresh the table.

It is noticeable the cache is updated, but it is not triggering to update the react components.

me-andre commented 7 months ago

I can confirm that in 3.9.4 when I update the cache by providing correct id and __typename the component receives stale data while I can clearly see that the cache has the fresh one.

phryneas commented 7 months ago

I'm sorry, but without a reproduction, we really cannot do a lot about this. We can't reproduce this on our end, so we really rely on someone providing us with a minimal reproduction of the issue.

jnsandrew commented 4 months ago

Apologies I don't have a reproduced repo and I'm not sure if/when I can find the time for it, but I had a similar issue - I'm trying to update the user's account, and without optimistic response, the UI re-renders correctly. The payload is something like:

mutation UpdateUserMutation($settings: JSON!) {
    updateAccount(input: { settings: $settings }) {
      id
      settings
    }
  }

When I try and use optimistic response, the component just re-renders with the stale data, but I can see the API response contains the new data, and the apollo chrome extension shows the correct data in the cache too.

What fixes it for me was to mutate some other piece of data as part of the mutation e.g. currency_code

mutation UpdateUserMutation($settings: JSON!, $currency_code: String) {
    updateAccount(input: { currency_code: $code, settings: $settings }) {
      id
      currency_code
      settings
    }
  }

Now optimistic response of:

optimisticResponse: {
      updateAccount: {
            id: account.id,
            currency_code: newCurrencyCode,
            settings: newSettings,
            __typename: 'Account',
       },
},

Will update the UI correctly. So my theory is that the equality check is failing when objects are involved / a new object isn't being created correctly when an object in the cache is updated?

I guess it's worth noting in my case, settings doesn't have a typename, it's just a freeform json field, so the cache doesn't have any refs/__typename to work with.

I know it isn't a solution (it's not always possible to update other data too) but hopefully the above is of some help to someone!

Edit In my particular case, I found there's an updated_at field in the data that auto updates with a new DateTime, so I don't have to actually mutate anything, just make sure updated_at is being used in the query and mutation.

jnsandrew commented 3 months ago

I've managed to reproduce this issue in a standalone repo:

https://github.com/jnsandrew/apollo-cache-error

In my case - I see the issue on storybook + mock service workers. Hope this helps to debug the issue. I'm not 100% sure if it's even an apollo thing yet, but it looks like that at the moment.

phryneas commented 3 months ago

@jnsandrew I've taken a look, and in your case you provoke an error by returning an incomplete response in your msw handler - a spec-compliant GraphQL server would never do that.

image

If you fix that response, it starts working as intended.

export const CompleteTask = {
  args: {},
  name: 'Complete task',
  parameters: {
    msw: {
      handlers: [
        graphql.query('TaskEntityListQuery', (req, res, ctx) =>
          res(
            ctx.data({
              taskEntityList: [
                {
                  avatar: null,
                  id: 2001,
                  completed: false,
                  created_at: '2023-11-27T09:45:00.274Z',
+                 entity_type_id: 1,
                  __typename: 'Task',
                },
              ],
            }),
          ),
        ),
jnsandrew commented 3 months ago

Doh! Thank you!

As a side note did you do anything special to get that error to show? As it doesn't come up in my console 🤔

phryneas commented 3 months ago

If you are not running the production bundle you should be seeing those error messages.

I know of one third-party "Apollo Client debugger" browser extension that enforces production mode (the official one doesn't do that!), so you might want to check your browser extensions.