-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Referential Integrity issue causes perf issues in Apollo 3 #6202
Comments
fetchMore
causes perf issues in Apollo 3
Quick solution: if you want referential equality here, you need to give those You seem to be working under the impression that the objects you write into the cache are the very same objects that the cache gives you back as result objects, as if the cache simply holds onto those objects temporarily, and later returns them to you. This is an incorrect interpretation of how the result caching system (#3394) works. The goal/promise of the result caching system is to give you the same ( In your reproduction, the only object with an ID is the All of which is to say: if you value referential identity preservation, you should make sure the objects in question are normalized in the cache. In situations where you don't care so much about fine-grained referential identity preservation, you can omit the necessary You could imagine the cache somehow canonicalizing unrelated result objects that happen to be deeply equal to each other, returning the same object reference automatically. This is one of the reasons I'm excited about the Happy to answer questions about this system, since these are subtle issues/tradeoffs, but I believe it is working as designed. |
Wow thanks a lot for the detailed answer. I was indeed not aware of some of the logic involved with the cache.
Perfect, good to know!
With the new trend on hooks, components tend to get bloated with memoization logic, so I'd prefer if I handled it outside the "context" of a React component. In our app we fetch a paginated list of heavy read-only data, that I'd explicitly like to not normalize, since they have no reason to be normalized (e.g. server logs displayed in a table). Based on what you said, the only way to have referential equality is to add normalization to hte game, but I feel that this contradicts the benefits described in the related section here. Specifically, this part:
I understand that an app-land solution for memoization can do the trick, but I'm just wondering if this is indeed out of the scope of Apollo or just a limitation that's tied to the implementation. Thanks again |
I do think some sort of final memoization/canonicalization step is in scope for future versions of Apollo Client. I'd like to see some more progress on the |
As for the performance tradeoffs of normalization, I think you're right to point out that contradiction. Specifically, I think our recommendation about disabling normalization for performance may have been somewhat speculative, and could easily be trumped by the downstream performance benefits of reference equality. Nowadays, I think of disabling normalization (with |
Gotcha, I'll run some tests, compare timings with and get back to you if something seems off. For the time being, since what I originally described is expected from your side, I think it's fair to close this issue. |
Whenever I use
fetchMore
to fetch more items from the server, I couple it withupdateQuery
in order to provide the merge strategy of the new data. A common practice is to "append" the new items into the existing list like so[...prevResult.data.items, ...fetchMoreResult.data.items]
.I would expect that this would retain referential equality of the existing items, since that happens in vanilla JS. Unfortunately, this doesn't happen when using Apollo, since the items array has objects that are all referentially different from the previous iteration. For example, If I had an array of one item:
and then by fetching more I received another new item
and I merged them in the
updateQuery
using the typical practicethen the
{ text: ' first' }
object would not be the same between renders, which means that React would have to re-render and anyReact.memo
would be rendered useless. In a scenario where we have 100 or 200 expensive components that need to un-necessarily re-render everytime something gets added to the array, this may cause real issues.Intended outcome:
I would expect the objects would not be changed in any shape or form, meaning that Apollo would not "clone" or in any way manipulate what I return from the
updateQuery
handlerActual outcome:
Apollo alters the data somehow and sadly they lose their referential integrity
How to reproduce the issue:
In this codesandbox you can clearly see the issue if you open the console.
Clicking "fetch more" continuously appends items to a list. You would expect that between renders the 1st item would be the same (since we' re only adding to a list), but unfortunately it's referentially different on each render pass.
https://codesandbox.io/s/apollo-reference-equality-1exes?file=/src/App.js
Versions
System:
OS: macOS 10.15.2
Binaries:
Node: 10.16.0 - /usr/local/bin/node
Yarn: 1.19.0 - /usr/local/bin/yarn
npm: 6.14.2 - /usr/local/bin/npm
Browsers:
Chrome: 81.0.4044.122
Firefox: 72.0.2
Safari: 13.0.4
npmPackages:
@apollo/client: 3.0.0-beta.41 => 3.0.0-beta.41
apollo-link-error: ^1.1.12 => 1.1.12
The text was updated successfully, but these errors were encountered: