question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Referential equality of individual items in a list is lost when appending a new normalized item to the cache

See original GitHub issue

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: https://github.com/apollographql/apollo-client/issues/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:

 const newMessageRef = cache.writeFragment({
      fragmentName: "TextMessage",
      fragment: TEXT_MESSAGE_FRAGMENT,
      data: result.data.sendMessage
 });

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

How 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/client@3.0.2 Browsers: Chrome: 84.0.4147.89 Firefox: 78.0.2 Safari: 13.1.1

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:7
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
niekertcommented, Jul 24, 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:

image

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!

3reactions
klaussnercommented, Dec 9, 2020

@ruipneves === equality guarantees for objects in the cache will be improved in version 3.4.0: https://github.com/apollographql/apollo-client/issues/4141#issuecomment-733091694.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Customizing the behavior of cached fields - Apollo GraphQL
You can customize how a particular field in your Apollo Client cache is read and written. To do so, you define a field...
Read more >
Check if item is already in a list with referential equality
I want to check if an item is already in a list with referential equality and not with strucural equality. For clarity: referential...
Read more >
Add a system property - Product Documentation | ServiceNow
When you change a system property value, the system always flushes the cache for the sys_properties table. Use this field to determine whether ......
Read more >
WebDriver - W3C
WebDriver is a remote control interface that enables introspection and control of user agents. It provides a platform- and language-neutral ...
Read more >
KEPServerEX Manual - Kepware
OPC ProgID has been added to the ProgID Redirect list. ... Write request rejected on read-only item reference '<name>'. ... This is a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found