You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the CodeSandbox reproduction below we built a chat application built with apollo-client. Whenever a new message is added to a chat, we update apollo cache using cache.modify to add the new message object (which has id and __typename) to an existing field.
What we noticed that every time a new message is added, the <Message /> components - which accept a message prop directly returned from useQuery will re-render, even thought the <Message /> component is wrapped in React.memo
We noticed that the object reference of each item in the array returned by useQuery (conversation.messages) changes, even though we are only appending a new normalized object to the cache, leaving other references untouched.
We did find this issue that looks similar: #6202 but it mentions that referential equality should be possible if the items in the array are normalized in the cache, which they should because they have an id and __typename and are not configured by keyFields
Our mutation does the following:
const useSendMessageEdgeMutation = (
conversationId,
{ useResultData = true }
) => {
return useMutation(SEND_MESSAGE_MUTATION, {
optimisticResponse: () => ({
sendMessage: {
__typename: "Message",
id: Date.now(),
content: "New message optimistic"
}
}),
update: (cache, result) => {
cache.modify({
id: cache.identify({
__typename: "Conversation",
id: conversationId
}),
broadcast: true,
fields: {
messages: existingMessages => {
// Usually when updating cache, you'd update it with the result data of some mutation
// If we update with result data of a mutation, the referential equality of each object in that list changes
// Because of that, each item rendered in this list loses the ability to React.memo
const data = useResultData
? result.data.sendMessage
: // When we hardcode the contents of the data written to cache, the problem we encounter
// Is not there. Only a re-render has to be done for the item thats added to the list
{
__typename: "Message",
id: existingMessages.length + 10,
content: "New message added"
};
const newMessageRef = cache.writeFragment({
fragmentName: "MessageFragment",
fragment: MESSAGE_FRAGMENT,
data
});
return [newMessageRef, ...existingMessages];
}
}
});
}
});
};
Intended outcome
Existing items in the list keep the same object reference even after adding an item to the list. We noticed that when we don't pass result.data.sendMessage in the writeFragment call, but rather a hardcoded value, the object references remain the same, like so:
Actual outcome
Each item in the list gets a new object reference, making it hard to use React.memo to prevent re-renders of components rendered as a result of the list
The text was updated successfully, but these errors were encountered:
niekert
changed the title
Referential equality of individual items in a list is lost when appending a new item to the cache
Referential equality of individual items in a list is lost when appending a new normalized item to the cache
Jul 23, 2020
After looking into this a bit more I found that optimisticResponse seems to be the cause of this issue. When enabled, the references of each item in the array changes once when the optimistic response is applied, and again when the actual result comes in.
When optimisticResponse is disabled, the object references of items in the array remain the same.
I tried to dive a bit deeper and what I'm seeing is that the cache store to executeSelectionSet for the optimistic response does include the Converastion object, but not the messages that were already in the cache:
Without optimistic response, this cache does include each of the messages that were written in the cache previously. I'm having a hard time understanding why this is the case but wouldn't mind investigating further. Thanks for having a look!
In the CodeSandbox reproduction below we built a chat application built with apollo-client. Whenever a new message is added to a chat, we update apollo cache using
cache.modify
to add the new message object (which hasid
and__typename
) to an existing field.What we noticed that every time a new message is added, the
<Message />
components - which accept amessage
prop directly returned fromuseQuery
will re-render, even thought the<Message />
component is wrapped inReact.memo
We noticed that the object reference of each item in the array returned by
useQuery
(conversation.messages
) changes, even though we are only appending a new normalized object to the cache, leaving other references untouched.We did find this issue that looks similar: #6202 but it mentions that referential equality should be possible if the items in the array are normalized in the cache, which they should because they have an
id
and__typename
and are not configured bykeyFields
Our mutation does the following:
Intended outcome
Existing items in the list keep the same object reference even after adding an item to the list. We noticed that when we don't pass
result.data.sendMessage
in thewriteFragment
call, but rather a hardcoded value, the object references remain the same, like so:Actual outcome
Each item in the list gets a new object reference, making it hard to use
React.memo
to prevent re-renders of components rendered as a result of the listHow to reproduce
Here is a Codesandbox that reproduces the issue: https://codesandbox.io/s/apollo-ref-re-render-issue-with-interface-8jvwh?file=/src/App.js:914-2772
Versions
@apollo/[email protected]
Browsers:
Chrome: 84.0.4147.89
Firefox: 78.0.2
Safari: 13.1.1
The text was updated successfully, but these errors were encountered: