apollo-client: Components never get updated after `writeData` or `writeQuery`
Intended outcome:
Components update automatically after cache modification with writeData
or writeQuery
.
You repeat it everywhere in your docs:
Any subscriber to the Apollo Client store will instantly see this update and render new UI accordingly.
Actual outcome:
Components never get updated after writeData
or writeQuery
.
You have to call broadcastQueries()
like so:
this.props.client.queryManager.broadcastQueries();
in order to force your <Query>
tags to reload themselves to render your new cache data.
But after you call broadcastQueries()
a new issue arise - it reloads ALL your <Query>
tags, not just those that you need. Huge performance issue.
ANd you have nothing in your docs about it. Question here
How to reproduce the issue:
class FooComponent extends PureComponent {
componentDidMount() {
const query = gql`
query QQQ {
Me {
__typename
id
something
}
}
`;
// I call writeQuery manually
// How does <Text> input change instantly with new value?
// Or change it to writeData
this.props.client.cache.writeQuery({
query: query,
data: {
Me: {
__typename: 'User',
id: 'be2ae9d718c9',
something: 'ABSDEF'
}
}
});
}
render() {
let something = this.props.Something.something;
// How does <Text> input change instantly with new value
// after I call writeQuery in componentDidMount above?
return (
<View>
{/* HOW DOES IT CHANGE instantly after writeQuery or writeData ?? */}
<Query query={ GET_SOMETHING }>
{ params => {
const data = get(params, 'data.Me.something', {}) || {};
const something = data.something;
return (
<Text>
{something}
</Text>
)
} }
</Query>
</View>
)
}
}
export default compose(
withApollo,
graphql(getSomethingQuery, {
name: 'Something'
}),
)(FooComponent);
Versions
System:
OS: macOS High Sierra 10.13.4
Binaries:
Node: 8.11.3 - /usr/local/bin/node
npm: 5.6.0 - /usr/local/bin/npm
Browsers:
Chrome: 68.0.3440.106
Safari: 11.1
npmPackages:
apollo-boost: ^0.1.4 => 0.1.10
apollo-cache-inmemory: ^1.1.0 => 1.2.5
apollo-client: ^2.0.3 => 2.3.5
apollo-link: ^1.0.3 => 1.2.2
apollo-link-http: ^1.2.0 => 1.5.4
react-apollo: ^2.1.1 => 2.1.9
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 63
- Comments: 66 (5 by maintainers)
you need to use another instance of the data object like this
@wzup, This was happening to me and I (think) I fixed it by using
client.writeQuery
instead ofclient.cache.writeQuery
. I’m not sure what the difference between these two functions is - but when I use the former, the rest of the components are updated with the new, written results.I was just tearing my hair out over a regression where an existing component doesn’t consistently update anymore after a mutation call, update : writeQuery.
I tested render, and it appears not to receive new props after a writeQuery from the mutation update even though the cache seems to be right (but sometimes it does work).
I played around a bit and was relieved (and confused) to see it behaving properly and consistently after some very minor changes making the data write immutable. Here’s some examples:
Bad, flaky component re-rendering:
These very minor changes (can you spot them?) made the mutation update work flawlessly again:
Seems like it has something to do with immutability.
Here’s another example with writing for a delete mutation:
Not updating after mutating:
Works again:
So I’m pretty confused about why this works, but I’m hoping this account helps someone else or helps Apollo fix a bug.
Having similar problems. I observe the cache is updated, but a component that directly uses the Query does not re-render. client.queryManager.broadcastQueries(); does not help
Wow… this is a really really really poorly documented issue in Apollo.
If anyone is using hooks there is a change that you need to add the
{ returnPartialData: true }
option to theuseQuery
call on the dependant item containing the list. Check the API docsOne comment i will make about Apollo, its documentation is very poor. Time and time again i see so many developers struggling with simple issues that are due to a lack of documentation. Otherwise its an amazing library.
Short answer: just use
client.writeQuery
instead ofcache.writeQuery
when you want your changes to be reflected in the UI immediately.Long answer (from docs):
The
cache
you created withnew InMemoryCache(...)
class is not meant to be used directly, but passed to theApolloClient
constructor. The client then accesses thecache
using methods likereadQuery
andwriteQuery
. The difference betweencache.writeQuery
andclient.writeQuery
is that the client version also performs a broadcast after writing to the cache. This broadcast ensures your data is refreshed in the view layer after theclient.writeQuery
operation. If you only usecache.writeQuery
, the changes may not be immediately reflected in the view layer. This behavior is sometimes useful in scenarios where you want to perform multiple cache writes without immediately updating the view layer.The
update
function receivescache
rather thanclient
as its first parameter. Thiscache
is typically an instance ofInMemoryCache
, as supplied to theApolloClient
constructor whent the client was created. In case of theupdate
function, when you callcache.writeQuery
, the update internally callsbroadcastQueries
, so queries listening to the changes will update. However, this behavior of broadcasting changes aftercache.writeQuery
happens only with theupdate
function. Anywhere else,cache.writeQuery
would just write to the cache, and the changes would not be immediately broadcast to the view layer. To avoid this confusion, preferclient.writeQuery
when writing to cache.Source: https://github.com/apollographql/apollo-client/pull/4664/files
I was having the same issue. After reading @good-idea 's comment, I replaced
client.cache.writeQuery
withclient.writeQuery
and it started to work on my HOC (withApollo
component).Hmm, not sure that I have a similar issue. But, when I put
writeData
tosetTimeout
it works correctly:Other components are updated correctly with new state from
writeData
For me, I had to include variables into the query:
Query component
data
updated works after added correspondingvariables
options either onclient.writeQuery
orclient.cache.writeQuery
.Query componen t
withApollo wrapped component
data
will NOT get updateddata
UPDATE WORKS!!@chemicalkosek When you have trouble re-rendering after an update to the cache, 9 times out of 10 the problem is caused by mutating the cached data. In this case, you’re doing
data.rooms = ...
. If you change the first example to not modify the data from the cache, it should work:You can now pass an option to the InMemoryCache constructor which will freeze the cache data to help prevent problems like this from happening:
new InMemoryCache({ freezeResults: true })
. In apollo-client v3, thisfreezeResults
option will be enabled by default.I use client.resetStore(); to resolve this issue
I don’t know if it’s the best solution but it’s works for me
If anyone else is at the bottom of this thread and still confused… you really do need a deep clone of the new data object as per @rchasman 's investigation. But you also need to make sure you provide variables as per @JaosnHsieh . Neither of things were required in a previous version of react-apollo.
I’ve found a solution to this. It took a lot of digging around to find out how to get the parent to re-render. You can get the component to re-render by calling
update
in the Mutation props and/orrefetchQueries
to refetch the query and update local state.update
andrefetchQueries
will only update that specific component after the mutation. It will not re-render the parent or grand-parent components. If you want to reload the query (for a parent) in response to a user action, theQuery
component passes a method calledrefetch
that you can call or pass down to child components when you want that component to refetch it’s query.Here is an example.
You can read more on refetching here: https://www.apollographql.com/docs/react/essentials/queries.html#refetching
Hi guys! I still have the issue, I tried to use client instead of cache but still the same - the cache gets updated, but it does not update our components. We are using the following code:
Is any solution already out there for it? Becuae otherwise we have to always refresh the page to get correct data for us.
I tried all the immutability solutions here. I see that the cache is updated with the new data. But the UI is not yet updated at all.
Hey @rohindaswani - when you’re calling
writeQuery
, are you attempting to update local state (as in https://www.apollographql.com/docs/react/essentials/local-state.html).I found that all my queries that read from local state (rather than the remote server) do not get refreshed after calling
writeQuery
orwriteFragment
.My workaround was to explicitly call a local mutation that modifies the local state instead of writing to the cache/proxy directly with
writeQuery
orwriteFragment
. Since the state/data is written locally, calling the mutation directly doesn’t penalize performance or anything (imo anyway).Something like:
this was a pain in the ass.
BUT
client.cache.writeQuery({}) won’t work !!!
you have to use client.writeQuery({})
@rchasman I observed the same thing…
all our update functions were operating on
data
directly and then passing it towriteQuery
(I think this came from examples in their old docs) and when I upgraded to the latest version, they all stopped working so there must be something under the hood in relation to immutability 😭Did anyone come to a resolution with respect to this? I’m facing a similar problem in that when I add the updated data to the cache with
writeQuery
the component doesn’t re-render. I’m also facing a related problem in which I need the component’s parent to re-render as well since the underlying data has changed. Any thoughts on how to do that?Ok I’ve managed to make it work by modifying my update function Previous version:
After refactor:
That’s what worked for me. Still there must have been some change in apollo-client between 2.4.* and 2.6,* that prevents the former code to work
Please see https://github.com/apollographql/apollo-client/issues/4398. Long story short, this isn’t a bug - it’s by design. Hopefully PR https://github.com/apollographql/apollo-client/pull/4664 helps address this in the docs, but if anything still isn’t clear, please let us know (or better yet, fire over a new PR). Thanks!
Thanks for the tips here everyone. Had the same issue but solved it using @rchasman’s method. It didnt work at first but realised I had forgotten to use the variables for the query when using
writeQuery()
I’ve tried all of these and NONE of them work for me. So the next question is: how can you verify if the update issue is due to writeQuery not triggering an update, or is useQuery is not updating?
Or stated in other terms: is there any way of debugging if your cache is actually updating properly from a writeQuery without having a query firing?
u can use cache.writeQuery. I think The problem is due
Object.is
comparasion in pure components or hooks. I solved the problem using “immutable” logic indata
. lodash.cloneDeep works very good for this.@obedm503 thank you, that is an interesting point. I may be wrong, please correct me if so, but as I know React does not compare states especially by link during the change detection, it compares the virtual dom and by value. This means that only really changed parts of the dom-tree will be rerendered. https://blog.angularindepth.com/what-every-front-end-developer-should-know-about-change-detection-in-angular-and-react-508f83f58c6a
@dylanwulf Thank you, actually I was just reading about
freezeResults
the same time you posted this. Also abouthere and here https://github.com/apollographql/apollo-client/pull/4543
As others have pointed here, you have to do a deep copy of the data in order for it to re-render. Keep in mind that
Object.assign
doesn’t do a deep copy of an object.the solution for me was to spread the data in as @JaosnHsieh suggested: (https://github.com/apollographql/apollo-client/issues/3909#issuecomment-516457707)
@charles-leapyear - thanks for that hint. I was tearing my hair out with this last night, woke up this morning to see your comment. I too was working directly on the
data
object. Switched over to usingconst newData = {...data}
and it works perfectly now. Hope this might help someone. Cheers!I am also facing the problem its updated the cache but no updating the component
Tried everything above and nothing was working. Upgraded from 3.1.5 to 3.3.15 (latest as of time of writing) per @LucaProvencal 's suggestion above and the problem disappeared.
I overlooked this for far too long: make sure you are on the latest Apollo version. I switched from apollo-client ^2.6.3 to @apollo/client ^3.2.5 and made sure I am importing ApolloClient as follows:
import { ApolloClient } from '@apollo/client';
None of the above solutions were working for me until I did this. Cache updating appears to be working now.
I have the same problem, but the reason is that I called
client.stop()
by mistake.On man, I think I am gonna give up on apollo state after working on it for over a week! I almost managed to fix this issue by using
returnPartialData: true Which works great if you are switching between views on the same stack in Expo, but the moment you unmount the view (like in a modal) the whole thing goes back to its initial state. Back to redux so I can spend my time working on my important stuff!
Hey folks, if you’re using
@apollo/client
(AC3), please give the latest beta version (3.0.0-beta.46
) a try, as we’ve made some substantial improvements to the automatic broadcast of cache changes (#6221 recently, and #6050 before that, inbeta.40
).I can’t begin to say this update will solve all the problems that have been raised in this thread, but there’s a reasonable chance that many of them will be fixed/improved, I think.
Thanks for everyone’s patience while we fix this behavior once and for all.
@raelmiu you can inspect the cache before and after a read or write through the devtools
@nadzic I was in the same boat, using client.writeQuery instead of cache.writeQuery, deep cloning and other stuff helped me none. Only the solution provided by @rchasman worked. I think you should give it a try.
Well, that solution didn’t work for me, I had to deep clone the
readQuery
object for it to re-render in the components it had to.broadcastQueries Does not work for me