See a minimal reproduction here, since it isn't easy to explain: https://codesandbox.io/s/fervent-hawking-o2bt5
If you watch the console, you can see that when you click the button, the resolver is running and the new data is being written to the cache. However, the useQuery query in the UI (App.js) is not rerunning, and the data is stale as a result.
Expected result
Versions
@apollo/client @ 3.0.0-beta.20
@jthistle Ok, a fix for this issue has been published in @apollo/[email protected]. Please give it a try when you have a chance!
Thank you, I'll try it and let you know.
Ok, my demo is working great now! Thank you.
However, there is still a problem. If you have a look at this sandbox, you can see the map center has the __typename field at first. After an update its lost this field. Why has this disappeared? Isn't cache.writeData meant to just update what exists? If not, how can I just update it (I'm guessing it may involve querying it all first, then editing it, then rewriting it)?
I haven't dug into that sandbox yet, but I would recommend using cache.writeQuery or cache.writeFragment instead of cache.writeData. You might assume that cache.writeData avoids needing to have a query or fragment, but it actually creates a temporary DocumentNode behind the scenes and then calls one of the other methods, so you might as well use the other methods.
OK, I see. I'll try that and get back to you. Thanks again.
I'm having a really weird problem along the same lines... but I can't reproduce it in a sandbox :scream:. Can you see anything wrong with this setup?:
I have my client cache set up with this initial state:
const initialState = {
mapControls: {
viewMap: true,
zoom: 16,
center: {
lat: 0,
lng: 0,
},
},
};
````
And in my resolvers, I resolve a `mutation updateCenter(lat: Number!, lng: Number!): Boolean` like this:
```js
const resolvers = {
Mutation: {
updateCenter: (_root, { lat, lng }, { cache }) => {
const query = gql`
query mapCenter {
mapControls @client {
center {
lat
lng
}
viewMap
}
}
`;
const { mapControls } = cache.readQuery({ query });
// Some writing to cache occurs here but is irrelevant
return true;
},
}
// ...
}
The cache.readQuery line gives me an error. There is no message attached to this error, but it only happens if the query I use with it requests viewMap. If I request zoom it's fine. I'm at a loss as to what's causing this.
FWIW, I'm calling this mutation using useMutation from another component using this mutation:
mutation SetMapCenter($lat: Number!, $lng: Number!) {
updateCenter(lat: $lat, lng: $lng) @client
}
I have no idea what's happening :(
I believe I am running into the same (original) issue, but I am on apollo-boost – is there an easy way to use this fix without "ejecting"? @benjamn
edit: nevermind. I've migrated off boost to the newest beta (.24) and my issue remains, so perhaps it is something else. conceptually it appears very similar to the original post's codesandbox example
@benjamn any plans to fix issues with Apollo Local state soon? I've been running into several issues with it. I like the idea of being able to do GraphQL on both client and server but it just seems the local state side of apollo has a lot of issues.
@lifeiscontent See #5799? We are hoping to transition away from the AC 2.x local state implementation, though it's going to remain in the codebase for the initial AC3 launch.
This has a label of "Fixed in prerelease". I'm on V3.0.0-beta.36 and I still see this issue.
Mutation and query in the same component. When I mutate state, the query doesn't refresh even after calling refetchQueries
import gql from 'graphql-tag';
export const INVITE_FRAGMENT = gql`
fragment inviteFragment on LocalInvite {
id
cpf
phone
email
}
`;
export const QUERY_INVITES = gql`
query Local__Invites {
local__invites @client {
...inviteFragment
}
}
${INVITE_FRAGMENT}
`;
export const MUTATION_SET_INVITES = gql`
mutation Local__SetInvites($invites: [LocalInvite!]!) {
local__setInvites(invites: $invites) @client {
...inviteFragment
}
}
${INVITE_FRAGMENT}
`;
const typeDefs = gql`
type LocalInvite {
id: ID!
cpf: String!
phone: String!
email: String!
}
extend type Query {
local__invites: [LocalInvite!]!
}
extend type Mutation {
local__setInvites(invites: [LocalInvite!]!): [LocalInvite!]!
}
`;
const resolvers = {
Query: {
local__invites: (root, args, { cache, getCacheKey }) => {
const res = cache.readQuery({ query: QUERY_INVITES });
console.log('res1', res);
return res.local__invites;
},
},
Mutation: {
local__setInvites: (root, { invites }, { cache, getCacheKey }) => {
const newInvites = invites.map(i => ({
...i,
__typename: 'LocalInvite',
}));
console.log('write1', newInvites);
cache.writeQuery({
query: QUERY_INVITES,
data: {
local__invites: newInvites,
},
});
return newInvites;
},
},
};
export default {
typeDefs,
resolvers,
};
const MyComponent = () => {
const {
data: { local__invites },
loading,
error,
} = useQuery(QUERY_INVITES);
const [saveInvites, dataInvites] = useMutation(MUTATION_SET_INVITES);
const [activeDependent, setActiveDependent] = React.useState(null);
React.useEffect(() => {
saveInvites({
variables: {
invites: [
{
id: 123,
cpf: '213123',
email: '[email protected]',
phone: '1231',
},
],
},
refetchQueries: () => [
{
query: QUERY_INVITES,
},
],
});
}, []);
console.log(
'selected1',
local__invites,
loading,
error,
dataInvites?.data,
dataInvites?.loading,
dataInvites?.error,
);
return null;
};
export default OwnerInviteDependents;
The output sequence is:
selected1 Array [] true undefined undefined false undefined
selected1 Array [] true undefined undefined true undefined
res1 Object {
"local__invites": Array [],
}
write1 Array [
Object {
"__typename": "LocalInvite",
"cpf": "213123",
"email": "[email protected]",
"id": 123,
"phone": "1231",
},
]
selected1 Array [] false undefined undefined true undefined
res1 Object {
"local__invites": Array [],
}
selected1 Array [] false undefined Object {
"local__setInvites": Array [
Object {
"__typename": "LocalInvite",
"cpf": "213123",
"email": "[email protected]",
"id": 123,
"phone": "1231",
},
],
} false undefined
As I step through the code, I can see that immediately after writing the data, it's in the cache ('write1'). But the subsequent query ('res1') shows a cache that is empty. So, it seems like the cache reference is outdated or is reset.
We get an error if we use react-native-debugger similar to the one we got before we implemented the react-native-debugger network inspect fix. The same error doesn't occur with standard Chrome debugger.
Uncaught TypeError: Cannot read property 'get' of undefined
at /Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37
at Array.forEach (<anonymous>)
at t.value (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37)
at /Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37
at /Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37
at Array.forEach (<anonymous>)
at /Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37
at Array.forEach (<anonymous>)
at e.value (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:37)
at /Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:3
at Array.forEach (<anonymous>)
at WebSocket.e.onmessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:3)
at WebSocket.onMessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/EventTarget.js:99)
at WebSocket.emit (events.js:203)
at Receiver._receiver.onmessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/WebSocket.js:141)
at Receiver.dataMessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/Receiver.js:389)
2app.html:1 Uncaught SyntaxError: Unexpected token � in JSON at position 0
at JSON.parse (<anonymous>)
at WebSocket.e.onmessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/react-devtools-core/build/standalone.js:3)
at WebSocket.onMessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/EventTarget.js:99)
at WebSocket.emit (events.js:203)
at Receiver._receiver.onmessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/WebSocket.js:141)
at Receiver.dataMessage (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/Receiver.js:389)
at Receiver.getData (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/Receiver.js:330)
at Receiver.startLoop (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/Receiver.js:165)
at Receiver.add (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/Receiver.js:139)
at Socket.<anonymous> (/Applications/React Native Debugger.app/Contents/Resources/app.asar/node_modules/ws/lib/WebSocket.js:138)
at Socket.emit (events.js:203)
at addChunk (_stream_readable.js:295)
at readableAddChunk (_stream_readable.js:276)
at Socket.Readable.push (_stream_readable.js:210)
at TCP.onStreamRead (internal/stream_base_commons.js:166)
If instead of cache.writeQuery and cache.readQuery, I use client.writeQuery and client.readQuery I get the following slightly better, but still problematic output result
selected1 Array [] true undefined undefined false undefined
selected1 Array [] true undefined undefined true undefined
res1 Object {
"local__invites": Array [],
}
write1 Array [
Object {
"__typename": "LocalInvite",
"cpf": "213123",
"email": "[email protected]",
"id": 123,
"phone": "1231",
},
]
selected1 Array [
Object {
"__typename": "LocalInvite",
"cpf": "213123",
"email": "[email protected]",
"id": 123,
"phone": "1231",
},
] true undefined undefined true undefined
selected1 Array [] false undefined undefined true undefined
res1 Object {
"local__invites": Array [],
}
selected1 Array [] false undefined Object {
"local__setInvites": Array [
Object {
"__typename": "LocalInvite",
"cpf": "213123",
"email": "[email protected]",
"id": 123,
"phone": "1231",
},
],
} false undefined
Now the query gets an update, but the cache apparently remains empty or gets reset.
Just a follow up on my comments here. I create a code repo and I couldn't reproduce the issue there. So obviously, something else is going on. For those who might find it useful: https://codesandbox.io/s/naughty-glitter-wzmof The actual cause for my issue was an undetected remount of the ApolloProvider which lead to the cache being cleaned.
I've spent the past part of a day trying to understand why my cache was updating via the client.writeFragment but the update not being reflected in the UI/broadcast. Ultimately it was because a query was being triggered which had fetchPolicy: 'network-only' set. Just wanted to put it out there.
Most helpful comment
@benjamn any plans to fix issues with Apollo Local state soon? I've been running into several issues with it. I like the idea of being able to do GraphQL on both client and server but it just seems the local state side of apollo has a lot of issues.