Skip to content

Performance issue with many subscriptions via useQuery vs. useQueryState #5052

@brandongregoryscott

Description

@brandongregoryscott

Hey team!

I've been diagnosing some performance issues in an application and I came across a somewhat unexpected culprit. We're using RTK Query for API data fetching & caching, as well as some global UI state management that is not related to API data.

I found that the auto-generated useQuery hooks add some additional overhead that seems related to subscription tracking. On the page in question, there were only 10-12 endpoints being used but some of the endpoints had 1000-4000 subscriptions (found by logging out the endpoint name + query args + keys in the state.api.subscriptions object)

We load most of this data when the page is rendered and don't often refetch it from sub-components. As an experiment, I swapped some of the generated useGetXQuery hook calls with the useQueryState hook that is not auto-generated, i.e. api.endpoints.getUser.useQueryState, and found that the performance of mounting/unmounting components that called these hooks was much faster.

I wanted to raise the issue to see if there's any obvious reason for this extra overhead. All of the documentation seems to point to just using the generated query hooks and not reaching for the useQueryState / useQuerySubscription hooks available on each endpoint definition.

I've put together a minimal reproduction repo to showcase this issue. The UseQueryChild and UseQueryStateChild components are intentionally calling the respective hook multiple times for the sake of simulating how many consuming components our real application might have at one time. You'll notice that the conditional render of the UseQueryStateChild component is much faster than the UseQueryChild.

From my testing, it seems like useQueryState will still notify consuming components if the underlying data changes (i.e. something triggers an update to the cache), so is there any benefit to using useQuery over useQueryState if we prefetch the data at the top of the page tree and know that it will be cached by the time the consuming components use it (i.e. no additional API request needs to be fired)?

Reproduction repo: https://github.com/brandongregoryscott/rtk-query-subscription-performance

Reproduction demo:

RTK.Query.Subscription.Performance.Demo.mov

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions