why wouldnt Apollo client re-render UI after successful cache register? - reactjs

Just like the screenshot below, I am trying to add a task to my db and render it out on the client side as it updates. It does successfully readQuery and normalize the cache with my db, and adds to cache as below screenshot. The problem is the screen doesn't render again even after my code executes.
It does register new task correctly to the cache
Below is my code for this task.
// Apollo Client
await createBoard({
variables: { title, projectId },
refetchQueries: \[
{
query: GetBoardsDocument,
variables: { projectId },
},
\],
update: (cache, { data }) => {
const newBoardRes = data?.createBoard.boards;
const newBoard = newBoardRes && newBoardRes\[newBoardRes.length - 2\];
const existingBoards = cache.readQuery({
query: GetBoardsDocument,
variables: { projectId },
});
// console.log("newBoard", newBoard);
if (!existingBoards) return;
console.log("existingBoards", existingBoards);
console.log("newBoardRes", newBoardRes);
// cache.evict({ fieldName: "boards:{}" });
if (!newBoardRes) return; cache.writeQuery({
query: GetBoardsDocument,
variables: { projectId },
data: {
getBoards: {
boards: \[...existingBoards.getBoards.boards, ...newBoardRes\],
// boards: \[...newBoardRes\],
},
},
});
// console.log("handleCreateBoard", writeRes);
if (refetch) refetch();
},
});
};
I am confused as to why its not updating UI. From what I understand, apollo client updates its cache hence prop data changes, hence should react component rerender.
Am I missing something here?
Please advise.

Related

Apollo mutation that depends on a previous mutation in a for loop

I'm using react native and apollo client for an app that creates a chat given an array of selected users. My code looks like this:
const [addUser, {
data: userAdded, loading: addingUsers, error: errorAddingUsers,
}] = useMutation(ADDUSERTOCHAT)
const [makeChat, {
data: chat, loading: chatLoading, error: chatError,
}] = useMutation(NEWCHAT, {
variables: { ownerId: viewerId },
onCompleted: () => {
for (let i = 0; i < selectedFriends.length; i++) {
addUser({
variables: {
chatId: chat.newChat.id,
id: selectedFriends[i].id,
},
onCompleted: () => {
if (i === selectedFriends.length - 1) {
navigation.navigate('Chat', { chatId: chat.newChat.id })
}
},
})
}
},
})
Right now, this does not work. I am not sure how to run the addUser mutation only after the chat is created, and I'm also not sure if the for loop will work to run a mutation for every selected friend. I also need to navigate to the screen chat once everything in the process is done and I'm not sure if the condition I have will work for that. In sum, I'm a bit lost with how to sequence these mutations and can't get it to work. Any help is appreciated thanks!

Apollo Client cache does not update

I am using Apollo Server / Client and the cache does not seem to work on update mutations. (Create, Delete). The server gets updated but nothing happens on the front end. I have to reload the page to show the new item / show change of an item.
I followed the Apollo docs and modeled it after their sandbox implementation.
Let me know if you need more of my code, thank you.
Here is my code:
<form
onSubmit={(e) => {
e.preventDefault();
createUser(
{
variables: {
name: input.value,
email: input.value,
password: input.value
}
},
{
update(cache, { data: { createUser } }) {
cache.modify({
fields: {
allUsers(existingUsers = []) {
const newUser = cache.writeFragment({
data: { createUser },
fragment: gql`
fragment NewUser on User {
name
email
}
`
});
return existingUsers.concat(newUser);
}
}
});
}
}
);
}}
>
You need to provide an id property in the writeFragment method. Here's the example on the docs:
client.writeFragment({
id: '5',
fragment: gql`
fragment MyTodo on Todo {
completed
}
`,
data: {
completed: true,
},
});
Also, writeFragment returns void, so you need to use readFragment to get the data you want, or just use the data available in the mutation's result

Call a fetch API which is stored once in using apollo cache?

I have been working on Apollo GQL. I'm using apollo cache to reduce unwanted API calls. The probelm now I'm facing is when i have updated a data i should not re-fetch the data because the API is already called once and stored in cache.
Thing i wanted to do is either clear cache for a particular query or refetch the datas from server.!!
I can't clear the entire cache, cause i'm calling a lot of APIs
i have to re-fetch the data after the following mutation call.
const [
reopenInvoice,
{ loading: reopenLoading, data: reopenData, error: reopenError },
] = useMutation<IReopenData, IReopenVariables>(INVOICE_CLONE, {
onCompleted: ({ invoiceClone: { errors, status } }) => {
if (!errors || !errors.length) {
message.success("Invoice Reopened");
} else {
message.error(errors.join(" "));
}
},
});
"refetchQueries" is the simplest way of updating the cache.
https://www.apollographql.com/docs/angular/features/cache-updates/#refetchqueries
const [reopenInvoice, { loading: reopenLoading, data: reopenData, error: reopenError }] = useMutation<IReopenData, IReopenVariables>(
INVOICE_CLONE,
{
onCompleted: ({ invoiceClone: { errors, status } }) => {
if (!errors || !errors.length) {
message.success("Invoice Reopened");
} else {
message.error(errors.join(" "));
}
},
refetchQueries: [
{
query: TO_REFETCH_QUERY,
variables: {
id: objectID,
},
},
],
}
);

Reactjs/Apollo/AppSync Mutation Optimistic Response Resolved ID

So first off I will start by saying I added an optimistic response to my mutation so it would it stop producing duplicates as referenced here and from this previous S.O. question.
So that is all working but I have a set of dependant mutations that run after the first using async await.
submitForm = async () => {
// Only submit if form is complete
if (!this.state.saveDisabled) {
try {
// Optimistic Response is necessary because of AWS AppSync
// https://stackoverflow.com/a/48349020/2111538
const createGuestData = await this.props.createGuest({
name: this.state.name,
})
let guestId = createGuestData.data.addGuest.id
for (let person of this.state.people) {
await this.props.createPerson({
variables: {
name: person.name,
guestId,
},
optimisticResponse: {
addPerson: {
id: -1, // A temporary id. The server decides the real id.
name: person.name,
guestId,
__typename: 'Person',
},
},
})
}
this.setState({
redirect: true,
})
} catch (e) {
console.log(e)
alert('There was an error creating this guest')
}
} else {
Alert('Please fill out guest form completely.')
}
}
Now this works and it is using the same pattern for the mutation as per the sample project
export default compose(
graphql(CreateGuestMutation, {
name: 'createGuest',
options: {
refetchQueries: [{ query: AllGuest }],
},
props: props => ({
createGuest: guest => {
console.log(guest)
return props.createGuest({
variables: guest,
optimisticResponse: () => ({
addGuest: {
...guest,
id: uuid(),
persons: [],
__typename: 'Guest',
},
}),
})
},
}),
}),
graphql(CreatePersonMutation, {
name: 'createPerson',
}),
)(CreateGuest)
The only problem is that I can't force the state to get updated to the ID that actually gets inserted when using Async Await, so all the person entries get the place holder UUID. Note, I have also tried using id: -1 as is done with the createPerson mutation but that didn't change anything, it just used negative one for all the entires.
Is there a better way of doing this? I am doing something wrong. This all worked without the optimisticResponse but it always created two entries per mutation.
Can you try this again? There were enhancements to the AppSync SDK for Javascript which no longer require you to use Optimistic Response. You can use it optionally if you still want an optimistic UI.
Additionally you can also now disable offline if that's not a requirement for your app by using disableOffline like so:
const client = new AWSAppSyncClient({
url: AppSync.graphqlEndpoint,
region: AppSync.region,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey: AppSync.apiKey,
},
disableOffline: true
});

Apollo update after a mutation isn't triggering a rerender

I am having troubles with a mutation in graphQL apollo. When a page loads, it will run a query lectureResponseQuery and if the query == null a mutation fetchLectureResponseMutation is run to create a new document. This mutation returns the new result and I do an update to the query and I expect that the component will re-render with the new data, but it doesn't. Does anyone know why that is? Thanks!
#graphql(fetchLectureResponseMutation, {
options: ownProps => ({
variables: { lectureName: ownProps.match.params.lectureName },
update: (proxy, { data: { fetchLectureResponse } }) => {
const data = proxy.readQuery({
query: lectureResponseQuery,
variables: { lectureName: ownProps.match.params.lectureName },
});
data.lectureResponse = fetchLectureResponse;
proxy.writeQuery({
query: lectureResponseQuery,
data,
});
},
}),
name: 'fetchLectureResponse',
})
#graphql(lectureResponseQuery, {
options: ownProps => ({
variables: { lectureName: ownProps.match.params.lectureName },
}),
})
class LecturePage extends React.PureComponent {
componentWillUpdate(nextProps) {
if (nextProps.data.lectureResponse === null) {
this.props.fetchLectureResponse();
}
}
render() {
const { data } = this.props;
if (data.loading || data.lectureResponse === null) {
return <Loading />;
}
return <LectureLayout lectureResponse={data.lectureResponse} />
}
}
For anyone looking into this issue in the future- the central issue is that I wanted to do a find OR create operation. This works much better when the query just returns the new object if it doesn't exist because then you only make 1 backend call which means that you don't have to synchronize the timings between a query and a mutation.
TLDR: Use a query for a findOrCreate operation!

Resources