I have a very simple query:
query unreadChatMessagesCount {
unreadChatMessagesCount
}
unreadChatMessagesCount
defined in the schema asInt!
And I am using the following code to create a subscription for the query:
const q = this.props.client.watchQuery({
query: totalUnreadCountQuery,
});
q.subscribe({
next: console.log,
error: console.log,
});
I am using the default
fetchPolicy
, without polling.
The initial execution of the query works as expected and the result is printed to the log.
Then, my code does:
const data = client.readQuery({ query: totalUnreadCountQuery });
data.unreadChatMessagesCount = data.unreadChatMessagesCount + 1;
client.writeQuery({ query: totalUnreadCountQuery, data });
The store updates and I can see it's new value in the store, but the next
of the watchQuery
Observable is not triggered.
Also trying to execute it using react-apollo
- but the props does not update.
UPDATE:
I did some debugging, and I think that maybe a call for broadcastQueries
is missing after updating the store manually... adding this:
client.queryManager.broadcastQueries();
After the writeQuery
call did the trick and provides a temporary workaround.
Intended outcome:
Store should update, then trigger the next
of the Observable
, otherwise the subscribers can't tell that the data has changed.
Actual outcome:
Store updates with new value, but the Observable
's next
is not triggered.
How to reproduce the issue:
Use writeQuery
to update a query that created with watchQuery
(or with react-apollo
HOC)
Version
"apollo-cache": "^1.0.0",
"apollo-cache-inmemory": "^1.0.0",
"apollo-client": "^2.0.1",
"apollo-link": "^1.0.0",
"apollo-link-http": "^1.0.0",
"graphql-anywhere": "^4.0.0",
"graphql-tag": "^2.5.0",
"react-apollo": "^2.0.0",
@dotansimha can you create a reproduction of this?
Happens with writeFragment too (pretty clear it is, but maybe it worth mention it)
@jbaxleyiii
I created a reproduction here:
https://github.com/dotansimha/apollo-client-unhandled-expection
Under the directory write-query
.
Note that the console.log
here:
https://github.com/dotansimha/apollo-client-unhandled-expection/blob/master/write-query/index.js#L111
it called only once, but when you uncomment this line:
https://github.com/dotansimha/apollo-client-unhandled-expection/blob/master/write-query/index.js#L128
it called twice...
And yeah @20matan is right, it also happens with writeFragment
Confirming this happens to me too, and adding client.queryManager.broadcastQueries()
as mentioned above solves the issue!
@dotansimha thanks for the reproduction! I'll take a look!
This is fixed on master!
@jbaxleyiii
Not sure what is a result of this: Do we need to call client.queryManager.broadcastQueries()
or not?
Because its definitely not working without it.
Some context:
We are injecting an Apollo client to a React component, which will do the manual readFragment
/writeFragment
on each new message from websocket. So, we are not doing it in update
method.
Same here in my question https://github.com/apollographql/apollo-client/issues/3905
writeQuery
never update Query
components.
I had to call broadcastQueries()
in order to force to update.
i have the same question,i don't know why?some one else know the reason???also use broadcastQueries。
We'll all gather here until we find a solution :cry:
Version
"apollo-cache-inmemory": "^1.2.10",
"apollo-client": "^2.4.2",
"react-apollo": "^2.1.8",
Update
For some reasons, the above hacky solution didn't work for me.
The good news is, I found a proper solution. :smile:
The updateQuery
function that is passed to the render prop function of <Query>
does the magic.
Instead of manually writing data to the cache and adding more hacks to do the update, simply use the updateQuery
function as bellow.
Example
updateQuery(prevData => ({
...prevData,
thing: [...prev.thing, { ...myNewThing, __typename: 'ThingType' }]
}));
Version
"apollo-cache-inmemory": "^1.2.10",
"apollo-client": "^2.4.2",
"react-apollo": "^2.1.8",
This should be in the docs.
I have confirmed that the react-apollo Query
subscription doesn't receive a callback after a cache readQuery
+ writeQuery
following a mutation. This subscription in Query
is created from the ObservableQuery
provided by this.client.watchQuery(opts)
. Therefore this is not a react-apollo issue, just manifesting here.
I have pinged maintainers and requested this to be reopened.
/related and causes https://github.com/apollographql/react-apollo/issues/2099
I tried all the following:
updateQuery
updateQuery + client.queryManager.broadcastQueries()
readQuery + writeQuery
readQuery + writeQuery + client.queryManager.broadcastQueries()
None of the above made the Query
re-render.
I think I'm having a similar issue - it's only just appeared after reinstalling from npm, previous cache updates would cause react to re-render, but now, although apollo-cache IS being updated, these changes are not being flushed to react for whatever reason
apologies for not being more descriptive, here's my package.json in case it helps:
"dependencies": {
"apollo-cache-inmemory": "1.3.7",
"apollo-client": "2.4.4",
"apollo-fetch": "0.7.0",
"apollo-link": "1.2.3",
"apollo-link-core": "0.5.4",
"apollo-link-error": "1.1.1",
"apollo-link-http": "1.5.5",
"apollo-link-state": "0.4.2",
"apollo-link-ws": "1.0.9",
...
"react": "16.4.2",
"react-apollo": "2.2.4",
},
We are experiencing a similar issue, we have several mutations updating the cache in our project which all re-render as expected yesterday.
Today we ran a fresh npm install
(we have no package.lock
) and now one of the screens don't re render.
The cache is updating as expected, but the Query component does not trigger a re-render.
Here are our versions:
"apollo-boost": "0.1.18",
"apollo-link-context": "1.0.9",
"apollo-cache-inmemory":"1.3.7",
Let me know if you need any other information.
Got the same problem here
@jbaxleyiii is this issue on your radar?
We fixed the issue by changing some of our components that used the cached data from PureComponent
's to Component
's.
We recently upgraded react-native
, react
and react-apollo
(and a lot of other libraries) so it looks like something changed in the way they do the prop
comparison or the way the cache
works.
Hope this helps.
@SMJ93 please read the comments above. This is already confirmed NOT to be a react related issue, this is apollo-client
or upstream from there. Please see the above research comment https://github.com/apollographql/apollo-client/issues/2415#issuecomment-431109153
@rosskevin I have read the comments above. I am sharing what fixed it for us in case it helps someone else.
You can see a problem with writeQuery
in apollo-upload-examples. I updated the dependencies recently and after mutations uploads no longer appear in the list.
Same as above, writeQuery
doesn't trigger re-render. Cache updates as expected. Weirdly this is only the case for 1 of my writeQuery
calls. Rest of them works just fine for the same query.
@SMJ93 Could you provide the apollo-client
version you are using after you got this to work ?
@okankayhan we are using apollo-boost
which includes apollo-client
:
"apollo-boost": "0.1.18"
hey @hwillson do you know if anyone is going to pick this up?
I can second what is listed above, but I am not sure that the cache is updating as expected:
Same as above,
writeQuery
doesn't trigger re-render.
However, I would tend to believe that it is updating properly. If I trigger a hot module reload, such that the component code reloads in the client, the query is again triggered and the view updates accordingly... Which leads me to believe that the query is not being triggered or re-rendered after a mutation update.
// Mutation
onRemoveItem(id) {
this.$apollo
.mutate({
mutation: ItemRemoveMutation,
variables: {
input: { id }
},
update: store => {
const itemId = this.$route.params.id;
const data = store.readQuery({
query: ItemsQuery,
variables: { id: itemId }
});
store.writeQuery({
data: {
...data,
item: {
...data.item,
itemSubSet: {
...data.position.itemSubSet,
edges: data.position.itemSubSet.filter(({ node }) => node.id !== id)
}
}
},
query: PositionVolunteersQuery,
variables: {
id: positionId
}
});
// Doesn't help
apolloClient.queryManager.broadcastQueries();
}
});
}
// Query
apollo: {
items() {
return {
query: ItemsQuery,
variables: { id: this.$route.params.id },
update({ item }) {
console.log("update"); // Not called after mutation
return item.itemSubSet.edges.map(({ node }) => ({
// Mapping stuff
}));
}
};
}
}
I am running into the same issue. In my case, a mutation followed by a writeQuery
updates the cache but the component using the query is not refreshed. Here's my code:
<Mutation mutation={CREATE_AUTHOR}>
{createAuthor => (
<AuthorDialog
author={this.editedAuthor}
onSave={author => {
createAuthor({
variables: {name: author.name},
// ----- Update AuthorsQuery in Apollo cache -----
update: (store, { data: { createAuthor } }) => {
const data = store.readQuery({query: AuthorsQuery});
data.authors.push(createAuthor);
store.writeQuery({query: AuthorsQuery, data});
}
// -----------------------------------------------
});
this.hideAuthorDialog();
}}
onCancel={this.hideAuthorDialog}
/>
)}
</Mutation>
The fully reproducible example is in this repo. The code in question is here. To reproduce the error, simply run the client and the server and try to add a new author. While the author is added to the cache, the Author List is not refreshed.
Can someone please confirm that I am running into this same issue? Any update on a fix? Here are the versions I am using:
"apollo-cache-inmemory": "^1.3.11",
"apollo-client": "^2.4.7",
Can someone please confirm that the issue I described above is the same as this issue?
TIA
I'm avoiding this issue for now by not going to the cache:
const results = await this.props.client.query({
query: getVenuesQuery,
variables: {
name: inputValue,
},
fetchPolicy: 'network-only'
});
@savovs we use writeQuery
because we don't want to wait.
I'm trying to do some work on this, so far I've gathered the following information as of today:
_.cloneDeep
works around this - which may be the best indication of the root causeI'll see if I can expose this in current tests or add a test to reproduce. apollo-client is not my area of expertise so this is going to be a slog.
edit: and it was, I got lost down the rabbit hole of updating the build and getting ts to run on tests in #4261 but I am back to it now.
I believe I have proven the bug with an expanded unit test.
writeFragment
writeFragment
test to update an innocuous value without replacing the array, still worksclient.mutate
test to ensure a payload result propagates to an observerI took these two test cases that used writeFragment
, and added what I think is the typical workflow we are using with our mutations, namely readQuery
then writeQuery
and expecting a change. Both of my new tests fail to receive a change after writeQuery
.
Two areas of concern:
Someone with more expertise, perhaps @jaydenseric, @hwillson or @benjamn please take a look at my reproduction PR and let me know if I have set the right expectations in the test https://github.com/apollographql/apollo-client/pull/4264
My reproduction in #4264 was flawed. I have fixed the usage of the code and it proves that writeQuery
as well as the standard response from mutate
is indeed triggering the observable update.
With the PR, we have expanded confidence that standard usage of writeQuery
and mutate
is working right. With this, @danilobuerger helped me through slack and (at least on a standard mutate
), he noticed a common error wherein I forgot an id
in my payload.
So, I have no reproduction. I'll look through our codebase while this is fresh and see if I can uncover another test case.
I have re-checked every one of my writeQuery
cases, and they are all working. Someone else is going to have to expand on the unit tests and see if they can create a reproduction.
I suggest looking at my test changes in #4264 and going from there. Given all the information I have at this time, I suggest we can close this issue.
Hi @rosskevin, thanks for your efforts. I have a simple example that is still demonstrating this issue. Let's not close it at this time.
Here's my repo: https://github.com/nareshbhatia/graphql-bookstore
To reproduce the issue please follow the instructions here: https://github.com/nareshbhatia/graphql-bookstore/issues/1
@nareshbhatia Your issue is unrelated to writeQuery
.
As you annotated your AuthorsPanel
with @observer
, the following rule applies:
observer also prevents re-renderings when the props of the component have only shallowly changed, which makes a lot of sense if the data passed into the component is reactive. This behavior is similar to React PureComponent, except that state changes are still always processed. If a component provides its own shouldComponentUpdate, that one takes precedence.
As you only shallowly changed the authors
prop by pushing to it instead of creating a new array, a rerender will not be triggered.
Either pass in the whole data
object (which will change) or set a shouldComponentUpdate
accordingly.
Thanks so much, @danilobuerger. You must be a MobX master in addition to GraphQL 👍
I was able to fix my issue by sending the full data
object to the AuthorsPanel
.
Given this, I also agree that this issue can be closed.
I'm not sure this has actually been fixed. I created a reproduction of the issue that you can find here:
https://github.com/alexlafroscia/____apollo-watch-query-subscription-bug
https://alexlafroscia.github.io/____apollo-watch-query-subscription-bug/
While the application uses Ember, I do not use any of the Ember wrapper's functionality, instead just leveraging the Apollo Client error directly.
In my case, the first state that should be emitted from writeQuery
is ignored by the watchQuery
observable. As suggested in this thread, calling broadcastQueries
manually after updating resolves the problem (which you can simulate in my reproduction site).
@alexlafroscia I took one look at your repo and bailed out because it isn't the simplest possible reproduction, but a repo full of files.
This issue is proven fixed by the unit tests that explicitly measure this behavior. I expanded the tests for my own satisfaction just to be sure.
The behavior you describe is exactly the behavior you will get when the id of your writeQuery does not match that of the original observable.
If you think otherwise, I suggest you add a unit test as a PR to this repo.
I took one look at your repo and bailed out because it isn't the simplest possible reproduction, but a repo full of files.
Sure, fair point. It's a full Ember application. I will make it more clear in the README
where the relevant code lives and thoroughly comment through the source code how to follow it for those that aren't sure how the code in that file works.
The behavior you describe is exactly the behavior you will get when the id of your writeQuery does not match that of the original observable.
What does "id of your writeQuery" mean?
If you think otherwise, I suggest you add a unit test as a PR to this repo.
I understand that you have unit tests that show that the problem shouldn't happen, but you also have real people with real applications that see this bug happening in the latest release.
What does "id of your writeQuery" mean?
I understand that you have unit tests that show that the problem shouldn't happen, but you also have real people with real applications that see this bug happening in the latest release.
@alexlafroscia I don't mean this to be harsh but these statements mean to me that you don't understand how observables or writeQuery
would update the observables. Please read about the apollo cache normalization or watch the ample video content and direct questions to the slack channel or stack overflow. This is an issue tracker that is best reserved for bugs/features and specific commentary - not a personal help channel.
I understand that you have unit tests that show that the problem shouldn't happen, but you also have real people with real applications that see this bug happening in the latest release.
And we can't go on wild goose chases, else we would have thousand of open issues. I am happy to help and fix any problem, but we do need a minimal reproducible sample. If you have one (not ember, just apollo-client), feel free to open a new issue linking to this and I will take a look.
The issue outlined in the reproduction from https://github.com/apollographql/apollo-client/issues/2415#issuecomment-459545805 points to a shortcoming in our docs. Our docs are a bit misleading with regards to expecting cache.writeQuery
to always lead to watched queries being re-broadcast. If the following from the repro
this.apollo.cache.writeQuery({
query: allPosts,
data
});
is updated to be
this.apollo.client.writeQuery({
query: allPosts,
data
});
everything will work. This is because client.writeQuery
takes care of calling the additionally required client.initQueryManager().broadcastQueries()
automatically. Right now our docs lead people to believe that cache.writeQuery
will make this happen, which is sort of true, but it depends on where it's being used. If the repro was updated to use the mutate
update
option like this
@task
createPost = function*() {
const result = yield this.apollo.mutate(
{
mutation: createPost,
variables: {
content: "A new post"
},
update: (cache, { data: { post: newPost } }) => {
this.set("createdPosts", [...this.createdPosts, newPost]);
const data = this.apollo.cache.readQuery({
query: allPosts
});
data.allPosts = [...data.allPosts, newPost];
this.apollo.cache.writeQuery({
query: allPosts,
data,
});
}
},
"post"
);
}
then in that case calling cache.writeQuery
would trigger the necessary re-broadcast, since update
takes care of this.
Long story short, we should clarify this in the docs by adding a note somewhere in the Updating the cache section. I also think it would make sense to expose the Apollo Cache API in the left hand menu of the docs, down in the API section with Apollo Client
and React Apollo
. We could add extra details like this to those specific cache API docs, which would help with discovery.
I'll create a new issue to track this work.
@hwillson that did not do the trick for me, sadly.
Was having the same issue described here (and in many other issues). I got things working for me by using client.watchQuery
(simplified from actual implementation):
const MenuWrap = ({ client, children }) => {
const [showMenu, setShowMenu] = useState(false) // reflects initial value for apollo state.showMenu
client
.watchQuery({ query: clientShowMenu })
.subscribe(({ data: { showMenu } }) => setShowMenu(showMenu))
return <div style={{ marginLeft: showMenu ? 300 : 0 }}>
{ children }
</div>
}
// somewhereElse.js
const toggleMenu = client => {
const { showMenu } = client.readQuery({ query: clientShowMenu })
client.writeQuery({ query: clientShowMenu, data: { showMenu: !showMenu } })
}
I had to do this because my <Query />
implementation only got updated whenever the value I was changing (toggling a Boolean
) switch back to the initial value 😞. The above snippet sucks though, I'm having to keep track of changes and update local component state 👎
@rosskevin I have seen your comments around a lot of issues dealing with <Query />
not updating properly. Maybe a dumb question... but is it supposed to be used as a container for hydrating other components with data from local state? Sure does seem like it should.
I had faced this issue and was scratching my head over it for couple of hours. Here is what was happening
My components are laid out as below
----> <Parent Component> // Has <Query component>
----> <Intermediary Component>
----> <Children Component> // This is the place where I have a mutation
Expected result:
The changes I do to my cache from the Children Component
should re render Parent Component
and update the props
chain
Implementation
I had an object like below in my cache
data: {
project: [
name: 'myProject',
endpoint: 'http://localhost',
created_at: 'date',
]
}
In my update
callback of the mutation what I wanted to do is update a key
in the nested object (_endpoint
key inside project
in this case_). Here is how I did it
// Scenario when the re-render doesn't happen
The problem above is that I got the result from the cache and I modified it to the new value based on the mutation result. Now while saving it I was creating a new object using JSON.parse(JSON.stringify())
.
Sadly the above didn't work.
Next I swapped the operation. I created a new object (My understanding is that a new object is created if you do const newObj = JSON.parse(JSON.stringify(oldObj))
. Kindly correct me if I am wrong) out of what was there in the cache and modified it and saved it in the cache and it worked like a charm. Here is how the code looks
Hope this is useful for people who are facing this issue :)
Most helpful comment
The issue outlined in the reproduction from https://github.com/apollographql/apollo-client/issues/2415#issuecomment-459545805 points to a shortcoming in our docs. Our docs are a bit misleading with regards to expecting
cache.writeQuery
to always lead to watched queries being re-broadcast. If the following from the reprois updated to be
everything will work. This is because
client.writeQuery
takes care of calling the additionally requiredclient.initQueryManager().broadcastQueries()
automatically. Right now our docs lead people to believe thatcache.writeQuery
will make this happen, which is sort of true, but it depends on where it's being used. If the repro was updated to use themutate
update
option like thisthen in that case calling
cache.writeQuery
would trigger the necessary re-broadcast, sinceupdate
takes care of this.Long story short, we should clarify this in the docs by adding a note somewhere in the Updating the cache section. I also think it would make sense to expose the Apollo Cache API in the left hand menu of the docs, down in the API section with
Apollo Client
andReact Apollo
. We could add extra details like this to those specific cache API docs, which would help with discovery.I'll create a new issue to track this work.