query icon indicating copy to clipboard operation
query copied to clipboard

Devtools are re-rerendering constantly on every cache update even when closed

Open pristas-peter opened this issue 3 years ago • 15 comments

Describe the bug

We are encountering performance issues with the app when using devtools with many queries (around 1000). We are using normalised cache keys, when for example fetch of list items updates data in each single fetch query via setQueryData.

However in v4, by using useSyncExternalStore things got really slow (app freezes for a short time when doing updates).

Is it possible to update devtools by batches, or is it even necessary to "observe" every query when the devtools are closed?

Your minimal, reproducible example

Steps to reproduce

Expected behavior

How often does this bug happen?

No response

Screenshots or Videos

No response

Platform

react-query version

v4 3.2.1

TypeScript version

4.7.4

Additional context

No response

pristas-peter avatar Aug 29 '22 09:08 pristas-peter

Please provide a codesandbox reproduction! By using useSyncExternalStore, we actually fixed a perf issue in the devtools:

  • https://github.com/TanStack/query/issues/2742#issuecomment-993923506

TkDodo avatar Aug 29 '22 11:08 TkDodo

Hi, check it out here: https://codesandbox.io/s/suspicious-jasper-xh7k88?file=/src/App.js

Try to scroll beyond 500 items or so, the app will become un-responsible. If you comment out react query devtools component, all is smooth.

This was not happening in react-query 3.

Thanks for checking it out.

pristas-peter avatar Aug 29 '22 19:08 pristas-peter

i can confirm that this is not how it should be :)

Given that it gets worse with the number of queries, my best guess is that all uSES subscriptions are being triggered to re-render even though they should not. We've switched to fine-grained subscriptions because they are supposed to be more performant. Before that, every update would re-render the whole devtools, which is why we had the workaround of not subscribing at all when the devtools are closed.

I see a couple of ways forward:

  1. re-add the workaround that we removed in #2742. Basically, render null when the devtools are closed. That would be a quick win, and probably not wrong because why would we subscribe to anything if we are closed. However, app would still lag with open devtools.
  2. investigate what re-renders so often and why, and stop those re-renders.
  3. if the renders are not coming from uSES subscriptions themselves but from top-down renders, we could try memoizing each query row. But I don't think that's the case.

I'm afraid I won't have much time to look into this right now. Would you like to contribute any of those steps @pristas-peter ?

TkDodo avatar Aug 29 '22 19:08 TkDodo

Yes, I will try to take a look at it next week

pristas-peter avatar Aug 30 '22 07:08 pristas-peter

i can confirm that this is not how it should be :)

Given that it gets worse with the number of queries, my best guess is that all uSES subscriptions are being triggered to re-render even though they should not. We've switched to fine-grained subscriptions because they are supposed to be more performant. Before that, every update would re-render the whole devtools, which is why we had the workaround of not subscribing at all when the devtools are closed.

I see a couple of ways forward:

  1. re-add the workaround that we removed in speedup devtools #2742. Basically, render null when the devtools are closed. That would be a quick win, and probably not wrong because why would we subscribe to anything if we are closed. However, app would still lag with open devtools.
  2. investigate what re-renders so often and why, and stop those re-renders.
  3. if the renders are not coming from uSES subscriptions themselves but from top-down renders, we could try memoizing each query row. But I don't think that's the case.

I'm afraid I won't have much time to look into this right now. Would you like to contribute any of those steps @pristas-peter ?

Hey @TkDodo, Can I work on this?

dhrjarun avatar Sep 09 '22 14:09 dhrjarun

I am not working on it and I don't know if @pristas-peter is doing anything, so yeah, go ahead 🚀

TkDodo avatar Sep 09 '22 14:09 TkDodo

cool

dhrjarun avatar Sep 09 '22 14:09 dhrjarun

Sorry guys, I was little busy this week as well. @DhrjArun if you find out something, let us know 💯

pristas-peter avatar Sep 09 '22 18:09 pristas-peter

sure @pristas-peter

dhrjarun avatar Sep 10 '22 05:09 dhrjarun

Not rendering DevToolPanel if closed solves the issue temporary.

I tried adding React.memo to QueryRow and DevToolPanel Components, but it didn't fix the issue.

re-renders are coming from uSES subscriptions, and it’s being called even if theres no change in the queryCache

In this case, on each next fetch, app gets 100 new items, thats why after 4 - 5 fetch app becomes unresponsive. I think the issue can be solve by virtualizing the list(QueryRows).

dhrjarun avatar Sep 10 '22 11:09 dhrjarun

Hi @TkDodo what should I do?

dhrjarun avatar Sep 10 '22 15:09 dhrjarun

Not rendering DevToolPanel if closed solves the issue temporary.

I think this is something we should do in any case. There is no point in keeping subscriptions when they are closed, and whatever future issues might pop up, users can always work around it by closing the devtools.

re-renders are coming from uSES subscriptions, and it’s being called even if theres no change in the queryCache

It would be good to find out which subscription has the problem, as we have multiple, fine-grained subscriptions:

https://github.com/TanStack/query/blob/ea80700c2c8d48c6d0cb4ae0165a424ebea5d35c/packages/react-query-devtools/src/devtools.tsx#L706-L731

Theoretically, as long as the result returned from the selectors doesn't change, it shouldn't trigger a re-render. But maybe it's the activeQueryState one?

@prateek3255 fyi

TkDodo avatar Sep 11 '22 11:09 TkDodo

I've merged the PR with the quick fix. However, I think it's still a problem when the devtools are opened that we should look into.

TkDodo avatar Sep 12 '22 08:09 TkDodo

Will check out what's wrong with the uSES subscription 👍

prateek3255 avatar Sep 12 '22 10:09 prateek3255

I think even if you remove all the uSES subscriptions except the first one. It will cause the same problem with 400-500 queryRows.

dhrjarun avatar Sep 12 '22 12:09 dhrjarun

any updates / progress on this issue @prateek3255 ?

TkDodo avatar Oct 29 '22 20:10 TkDodo

@TkDodo Was facing issues in local dev with linking devtools with one of the examples initially, but have got that figured out now. I will investigate this and share an update by today or tomorrow

prateek3255 avatar Oct 30 '22 07:10 prateek3255

@TkDodo So I ran React Profiler on the example above, and I think the culprit is not uSES here. The individual QueryRow's take significant time to render, since in the above example 100 queries are added to the cache in one go.

One thing I also noticed was that whenever new query rows were added all the query rows are re-rendered right now since the parent component re-renders. So I memoized the QueryRow component and saw some improvement in the render durations.

Here this is what the profiler looks when I load 400 rows with the way it is right now:

https://user-images.githubusercontent.com/21277179/198930719-4f04bc4d-f000-4f51-8094-93b1f2b8a39d.mov

In the above video notice with every commit (whenever new 100 rows are added), all the QueryRows are re-rendered, and the render time on the right increases with every commit significantly on the right.

Now this how the profiler looks like when I memoized the QueryRow component:

Screen Recording 2022-10-31 at 9.46.56 AM.webm

If you look closely you'll see the QueryRow components that have already been rendered are greyed out and only the new QueryRow components are rendered, also the render durations on the right are also less than what they were without memo. (For some reason the new QueryRow components take more time to render than the previous one which I am not sure why).

All though even with memo, I see improvement in the performance of the app, but it still isn't significant enough. I guess we should add the memo and keep rendering null when devtools is closed. What do you think?

prateek3255 avatar Oct 31 '22 04:10 prateek3255

I guess we should add the memo and keep rendering null when devtools is closed. What do you think?

Absolutely 💯

TkDodo avatar Oct 31 '22 07:10 TkDodo

@TkDodo opened a PR with #4416

prateek3255 avatar Oct 31 '22 16:10 prateek3255