Apollo-client: Component not re-render in apollo-link-state after upgrading apollo-cache-inmemory to 1.3.*

Created on 11 Oct 2018  ·  33Comments  ·  Source: apollographql/apollo-client

Intended outcome:

I use apollo link state to save an array of objects to the local client. In apollo-cache-inmemory 1.2.10, the component is able to re-render on pushing new items to the array or deleting one item from the array. After upgrading to 1.3.5, I expected everything would stay the same.

Actual outcome:

However the component fails to re-render at all with 1.3.*.

How to reproduce the issue:

apollo-cache-inmemory 1.2.10:

https://codesandbox.io/s/l99l9r1ml9 (There's an issue with codesandbox. I have to double click the buttons to trigger the re-render, but on my local, there's no such issue.)

apollo-cache-inmemory 1.3.5

https://codesandbox.io/s/8xqn9mz0l (It doesn't work at all, even if I double click either buttons)

Versions

"dependencies": { "graphql": "0.13.2", "react": "16.5.0", "apollo-cache-inmemory": "1.3.5", "apollo-link-state": "^0.4.2", "apollo-link-http": "1.5.5", "react-apollo": "2.2.4", "react-dom": "16.2.0", "apollo-client": "^2.4.2", "graphql-tag": "^2.10.0" },

Most helpful comment

Similar issue here going from 1.2.10 to 1.3.5, though I'm using readQuery/writeQuery in the update callback of a mutation. I was using .push to add something to a nested array and writing the data back, but nothing re-rendered. I now have to provide a new top level object to get it to re-render (which I can do with an immutability library, but will require updating hundreds of callbacks in our app).

I also did this based on the documentation examples mutating the cache results:

update: (proxy, { data: { createTodo } }) => {
      const data = proxy.readQuery({ query });
      data.todos.push(createTodo);
      proxy.writeQuery({ query, data });
    }

All 33 comments

Your first and second link have the same behavior for me.

I'm using the latest chrome version, i don't know if this is relevant but just throwing it out there.

@JoviDeCroock
Have you tried double clicking on the "Create" button at https://codesandbox.io/s/l99l9r1ml9
I don't know why but codesandbox doesn't re-render on first click. It works fine on the localhost.

Yes, maybe i should mention this the same behavior is that they both are working.

@JoviDeCroock
Just an update.
Looks like 1.3.5 doesn't work on Chromium (Arch Linux) with this example: https://codesandbox.io/s/8xqn9mz0l
It works on mobile Chrome.

However,
I've also tried the 1.3.5 version in react native but it also doesn't work. I see that the array has been updated in the memory, but the component just doesn't automatically re-render. No errors shown in the console.

That's a pretty odd issue, is there some way to reproduce it consistently for others aswell?

One more update:
The problem only occurs when it is an array. If the data is a string or integer, the re-rendering works as usual.
Example: https://codesandbox.io/s/71zm7m35o6

There is some info about this and how state changes are broadcasted in the local-state slack channel provided by @benjamn

Like to slack comment about a possible work around. https://apollographql.slack.com/archives/C87A16E8Y/p1539111592000100

@carllippert
Would you please post the workaround here? I cannot create an account to read. I do not have an @meteor.com email.

image

As a general note, you shouldn't be mutating the results returned from the cache, unless you are careful to re-write them immediately, since those objects may be returned for future reads. Make copies of the data you read from the cache, if you plan on changing the contents of the objects.

If I'm diagnosing this problem correctly, perhaps we should be Object.freeze-ing cache results?

Having the same issue.
1.2.10 - no problem, 1.3.5 did not rerender.
Instructions on screenshot didn't help.

~1.3.0-beta.15 did work, BTW~
sorry, mistaken. beta doesn't work either

@OurMajesty Can you put together a small reproduction?

Thank you for response, I'll try.

As a general note, you shouldn't be mutating the results returned from the cache

Tried to change push to concat and assignment to object spread, but no luck.

https://codesandbox.io/s/q7rkm6y1ow
something like this, but in real app I add new item to cache inside update callback of my backend mutation
1.2.10: items are added without a problem
1.3.5: items aren't added

@benjamn @OurMajesty
I have tried concat to clone the array in the 1.35 example, but still the component doesn't update.

The only way it can be worked, is to clear the cached data, then save the new data in React Native and Chromium. Like this (Example: https://codesandbox.io/s/wnxmzmm7o7):
cache.writeData({ data: { filterList: [] } }); cache.writeData({ data: { filterList: [...filterList] } });

@benjamn If that's the case (we shouldn't be mutating the cache results) then we need to update the docs, which _are_ mutating the cache results.
https://www.apollographql.com/docs/react/features/optimistic-ui.html#optimistic-advanced

So if this is expected behavior, then it is surely a breaking change, right?

Similar issue here going from 1.2.10 to 1.3.5, though I'm using readQuery/writeQuery in the update callback of a mutation. I was using .push to add something to a nested array and writing the data back, but nothing re-rendered. I now have to provide a new top level object to get it to re-render (which I can do with an immutability library, but will require updating hundreds of callbacks in our app).

I also did this based on the documentation examples mutating the cache results:

update: (proxy, { data: { createTodo } }) => {
      const data = proxy.readQuery({ query });
      data.todos.push(createTodo);
      proxy.writeQuery({ query, data });
    }

If I do writeQuery in update method of mutation It doesn't call rerender of my component with query like in writeQuery

I believe this has been fixed with [email protected] and [email protected]. To confirm, I updated @OurMajesty's reproduction by changing just those versions, and everything seems to work now.

Please try updating those packages, and comment here with your findings (good or bad)!

UPD: That was my problem, ensure you update both apollo-client and apollo-cache-inmemory

~So, it worked, thanks! But i had to change some code, to ensure I don't mutate anything like I did before.~

nevermind, the problem was because I didn't update apollo-client to 2.4.3, just apollo-cache-inmemory
thanks again

I have an issue with updating an array of object in cache in 1.3.6. The component will only re-render when the logs are cloned before unshifting a new item to the array.

Working example:
<Mutation update={(cache, {data: {createLog}}) => { let {logs} = cache.readQuery( {query,variables}); logs = [...logs]; if (logs) { logs.unshift(createLog); cache.writeQuery({ query, variables, data: {logs} }); } }} mutation={someMutation}/>

The component will not re-render when the logs are cloned after unshifting. How could this happen? Same things happen on Chromium and Mobile Chrome.

<Mutation update={(cache, {data: {createLog}}) => { let {logs} = cache.readQuery( {query,variables}); if (logs) { logs.unshift(createLog); cache.writeQuery({ query, variables, data: {logs:[...logs]} }); } }} mutation={someMutation}/>

@stonecold123 Can you update your CodeSandbox reproduction above to demonstrate the issue?

@benjamn 1.3.6 did not fix a similar issue we had after upgrading to 1.3.5. We don't have the issue with 1.2.10. We observed that changing direct fields of an object works just fine but any field with nested fields (in our case with two level nesting) causes issues. The data we get from query was not changed.
Example:

data.foo = {
  x: 5,
  y: "bar",
  z: {
    a: "5"
    b: {
       field1: "value1"
       field2: "value2"
    }
  }
}

Changes to x, y are gine, but changes to z.b.field1 are not reflected back. I can provide a test case if you like.

@cagdastulek Please do throw together a CodeSandbox reproduction.

having the same issue

https://codesandbox.io/s/k027kz5243

I'm doing something very similar to the data.todos.push example in the docs.

The sandbox example is using apollo-link-state instead of an actual API endpoint. If I update the data in the link state's resolver for the mutation using cache.writeQuery, the component re-renders (see the commented out code in index.js).

However, if I comment that code out and just return the newly added item (similar to what the frontend would see as a response from our API) and instead use writeQuery in the update callback in the mutation (see List.js), the component doesn't re-render. If you change apollo-cache-inmemory to 1.2.10, it works fine, but it fails on 1.3.6.

I also threw a logging line in the update callback to check the data. My contacts list is getting updated (each time I click the button, the count goes up by one if you check the console), it's just not re-rendering.

I also ran into the similar issue when reading nested fields from the store. Everything in the store is correct after calling a mutation, but still getting the old data in the Query component.

After diving into node_modules/apollo-cache-inmemory, I find out that it gets correct new data from the store when I removed the following lines:

https://github.com/apollographql/apollo-client/blob/31b0ee4a027c60394cf9297df8f9eeeda3918272/packages/apollo-cache-inmemory/src/readFromStore.ts#L110-L154

@dmarkow I believe your problem is fixed in [email protected], thanks presumably to #4069.

@chentsulin Not sure I can help you without a reproduction. Please make sure you're using the latest versions of apollo-client, apollo-cache-inmemory, apollo-utilities, react-apollo, etc.

@benjamn It sure does. Thank you!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stubailo picture stubailo  ·  3Comments

skeithtan picture skeithtan  ·  3Comments

stubailo picture stubailo  ·  3Comments

elie222 picture elie222  ·  3Comments

gregorskii picture gregorskii  ·  3Comments