Client - React, Apollo-Client, codegen
Server - Postgraphile
The app view list of file objects.
Users can add new file.
I wrote a Query for getting the file objects from the server.
And a Mutation for adding new file.
they both works.
what Im trying to achieve is updating the cached files with the new file, and re-render the component that contain the FilesQuery hook.
I followed this doc: https://www.apollographql.com/docs/react/data/mutations/#the-update-function
but calling cache.writeFragment
keep throwing the error:
Error: Could not identify object {"id":"de5b015b-28a8-4066-a5ed-44dcead659ed","createdAt":"2022-10-14T14:51:14.874548645","updatedAt":"2022-10-14T14:51:14.874550045","originalFileName":"MY_FILE.xlsx",...}
Files Query
query FileUploads(
$offset: Int = 0
$first: Int = 10
$orderBy: [FileUploadsOrderBy!] = [CREATED_AT_DESC]
$filter: FileUploadFilter
) {
fileUploads(
first: $first
offset: $offset
orderBy: $orderBy
filter: $filter
) {
totalCount
nodes {
id
uploaderOrgId
uploadFileType
counterPartiesCount
parsedPointsCount
effectiveDate
originalFileName
configFileName
createdAt
status
partiesIds
parsedFileUrl
uploadingOrg {
id
shortName
}
entitiesAndCps {
entities {
id
shortName
}
cps {
id
shortName
}
}
}
}
}
Table Compoent (Calls FileUploadsQuery)
const FileUploadsTable = () => {
const { data, loading, error } = useFileUploadsQuery();
...
}
Mutation - Update
update(cache, { data }) {
cache.modify({
fields: {
fileUploads(existingFileUploads = {}) {
const newFileUplodsRefs = (
data?.uploadToService?.result as any[]
).map((file) => {
return cache.writeFragment({
data: file,
fragment: gql`
fragment NewFileUpload on FileUpload {
id
uploaderOrgId
uploadFileType
counterPartiesCount
parsedPointsCount
effectiveDate
originalFileName
configFileName
createdAt
status
uploadingOrg {
id
shortName
}
entitiesAndCps {
entities {
id
shortName
}
cps {
id
shortName
}
}
}
`,
});
});
return [
...(newFileUplodsRefs as any[]),
...(existingFileUploads?.nodes || []),
];
},
},
});
},
EDIT 1
i managed to pass the error by passing:
cache.writeFragment({
id: `FileUpload:${file.id}`, <--
data: file,
fragment: gql`
fragment NewFileUpload on FileUpload {
id
}
`,
});
also changed the return object to:
return {
nodes: [
...newFileUplodsRefs,
...(existingFileUploads?.nodes || []),
],
};
still i don't see the re-render, in the component that display the FileUploads
.
(the component is FileUploadsTable
)
Solution
apolloClient
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import isEmpty from 'lodash/isEmpty';
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
shells: {
keyArgs: (args, context) => {
const {
variables: { filter, runFilter } = {},
fieldName,
} = context;
return `${fieldName}${
isEmpty(filter) && isEmpty(runFilter)
? ''
: ':' + JSON.stringify({ filter, runFilter })
}`;
},
merge(existing = [], incoming, { args, cache, variables }) {
const { offset = 0 } = args;
if (Array.isArray(existing)) {
return incoming;
}
const merged = { ...existing, nodes: existing.nodes.slice(0) };
for (let i = 0; i < incoming.nodes.length; ++i) {
merged.nodes[offset + i] = incoming.nodes[i];
}
return merged;
},
},
},
},
},
});
export const apolloClient = new ApolloClient({
cache,
link: new HttpLink({
uri: `${
typeof window === 'undefined' ? 'http://localhost:3009' : window.origin
}/frm/api/graphql`,
}),
defaultOptions: {
query: {
fetchPolicy: 'cache-first',
},
},
});
Shell Component
const loadMoreShells = async (page: number) => {
setIsPageLoading(true);
await fetchMore({
variables: {
first: SHELLS_PER_PAGE,
offset: SHELLS_PER_PAGE * (page - 1),
filter: { ...filter, ...{ pinned: { equalTo: false } } },
runFilter: runFilter ? { run: runFilter } : undefined,
orderBy: [
STATUS_ASC,
SHELL_RUNS_MAX_CREATED_AT_DESC,
EXECUTION_FROM_DESC,
],
},
});
};