I am trying to figure out how the normalization in Apollo allows react to rerender components. I have a query that gets a list of visitors (simplified)
visitList{
id,
checkInEntries{
id,
checkInTime,
checkOutTime
},
visitor {
id,
firstName
}
}
Basically the "visitList" is an individual visit. The visitor is information about the person, and "checkInEntries" is how many time they've been in the building. If they checked in there's a value in "checkInTime", else it is null. Same with checkOutTime.
The list is rendered in a quite normal fashion:
visitors.map(
visit => <VisitorItem
updateVisitorAfterCheckin={this.updateCacheAfterCheckin}
visitor={visitor} />
)
Each
The mutation:
mutation CheckVisitorIn($checkInTime: Date!, $wasAdHoc: Boolean!, $checkInEntryId: ID!, $visitId: ID!) {
createCheckInEntry(checkInEntry: {id: $checkInEntryId, checkInTime: $checkInTime, wasAdHoc: $wasAdHoc, visit: $visitId}) {
id,
checkInTime,
wasAdHoc,
visit {
id,
visitor {
id,
firstName
}
}
}
}
The checkInEntry id is generated client side as a GUID, and the visitId is the id of the visit that I tie the check-in entry to.
It is triggered with:
let checkInEntryGuid = Guid.create();
this.props.mutate({
variables: {
checkInTime: moment().toISOString(),
wasAdHoc: false,
checkInEntryId: checkInEntryGuid,
visitId: this.props.visit.id
},
update: (store, {data: {createCheckInEntry}}) => {
this.props.updateVisitorAfterCheckin(store, createCheckInEntry, this.props.visit.id);
},
});
This works fine. The update function triggers a function in
updateCacheAfterCheckin = (store, checkInEntry, visitId) => {
const data = store.readQuery({
query,
variables: {
limit: 30,
offset: 0,
before: ((d = new Date()) => { d.setDate(d.getDate() + 1); return d.toISOString(); })(),
after: new Date().toISOString(),
}
});
const checkedInVisitor = data.visitList.find(visit => visit.id === visitId);
checkedInVisitor.checkInEntry = [checkInEntry];
store.writeQuery({ query, data });
};
To the best of my knowledge the store is updated. All the variables (limit, offset, dates) are obviously the same throughout.
Intended outcome:
The
Actual outcome:
Nothing happens. If I want to update the visitor in question, the only fix I have found is to do a refetchQueries with the visitList, but that fetches all the visitors. Subsequently it doesn't seem like I can do optimisticUI changes if the store doesn't understand what is going on.
It may actually just be me who is doing this all wrong. I tried reading the docs again and again, but there aren't really a lot of examples regarding how to do this. To my understanding the way Apollo normalizes is to save a key with the query (so the visitList(....)), and maybe it has to do with the "checkInEntry" being a nested part of that query, and then mutated upon in isolation in the VisitorItem, and then Apollo doesn't know where to put the "new" data. But I'm not really sure. I've also tried doing a simply example just with a "stupid" example where there is no nested arrays, but just properties manipulated in the childs of the list. I couldn't get that to work either.
Hope it is understandable.
Version
//Thanks
I might be having the exact same result although I'm not trying to update a nested object (but I guess that shouldn't be a problem either).
From the docs it seems this update fn should re-render the component:
In order to change the data in your store call methods on your DataProxy instance like writeQuery and writeFragment. This will update your cache and reactively re-render any of your GraphQL components which are querying affected data.
@DennisBaekgaard
Change your writeQuery to this.
store.writeQuery({
query,
variables: {
limit: 30,
offset: 0,
before: ((d = new Date()) => { d.setDate(d.getDate() + 1); return d.toISOString(); })(),
after: new Date().toISOString(),
}
data
});
https://github.com/apollographql/react-apollo/issues/1746#issuecomment-370263274 should help with this. Closing - thanks!
Most helpful comment
@DennisBaekgaard
Change your writeQuery to this.