So I have a station
in my app. Here's an example object:
interface Station {
id: number; // auto-increment
stationId: number; // this is a constant
name: string;
status: 'live' | 'draft';
}
When updating the station
the stationId
will never change, but sql will auto increment the id
field. The station's status
will also be updated to 'draft'
. e.g.
const previousStation: Station = {
id: 1,
stationId: 123456,
name: 'Example splet right',
status: 'live',
}
// ... then some sql-fu
const nextStation: Station = {
id: 2,
stationId: 123456,
name: 'Example spelt right',
status: 'draft',
}
Apollo client is unable to infer that nextStation
should replace previousStation
, so there are a lot of mutations that require readQuery/ readFragment.
I was hoping there may be a simpler solution, but the complexity arises from the id
changing. If I could find all references to its object-id in the normalised data cache and update those fields. Is something like this even possible?
Solved. The key was to use a read/write fragment, using the old object-id
, but passing in the new id as a reference. Then evict the old reference from the cache. It looked similar to this:
mutation({
variables,
update(cache, { data }) {
if (!data) {
return;
}
const { id: newId, __typename } = data.station;
const existing = cache.readQuery({
query: GET_STATION_QUERY,
variables: { id: variables.id }
});
if (existing) {
const newStation = {
...(existing.getStation || {}),
...(data.updateStation || {})
};
cache.writeQuery({
query: GET_STATION_QUERY,
variables: { id: newId },
data: { station: newStation }
});
}
const oldObjectId = `${__typename}:${variables.id}`;
const fragment = gql`
fragment MyFragment on SomeType {
station {
id
}
}
`;
const existingStationOnMyType = cache.readFragment({
id: oldObjectId,
fragment
});
if (existingStationOnMyType) {
cache.writeFragment({
id: oldObjectId,
fragment,
data: {
id: newId
}
});
cache.evict({ id: oldObjectId });
cache.gc();
}
}
});