-
First off: we have a pretty intense React application. We use Take a look below at me navigating between pages in our application. You can see a bit of lag in the transition. Again, it's worth noting that there are hundreds (at least) of query hook instances on this page. All of the assets on the screen, links in the side bar, and virtually everything else, probably have some RQ hook nested somewhere in them. See this video showing more in-depth: Does anyone have any ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 13 replies
-
I'd be happy to look into this with you. I always advocate for just calling useQuery wherever you need the data, but of course creating an observer is not for free. However, it shouldn't be that expensive I think. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your offering to help. And yes, it does behave like this in production as well. If you sign up with our application at air.inc, and add a lot of assets to 2 boards, I think you'll be able to replicate this. I'd love to do a screenshare with you. Can you find some time on my calendar? Hopefully sooner than later 😅 |
Beta Was this translation helpful? Give feedback.
-
@TkDodo I've put together a CodeSandbox showing our issue. When there are 100 items on the screen, with 8 observers in each item, at 6x slowdown, they take about a 1.2s to mount and about 750ms to unmount. You can see the entire thread is locked up for that time with all of the event listeners being added/removed. |
Beta Was this translation helpful? Give feedback.
-
Update: After talking with @TkDodo (thanks for your help), we think the best approach here is to just reduce the number of usages of To show an example of our change, we previously had a component on our screen with a Before: // here's one of our reusable hooks that got used in a lot of places
const useSubscription = () => {
const { currentWorkspace } = useCurrentWorkspace();
const subscriptionKey = getSubscriptionKey(currentWorkspace?.id);
return useQuery(subscriptionKey);
}
// here's how it was used and this component could be rendered dozens of times
// and in a virtualized list so improving the mount/unmount time is super important
const myComponent = () => {
const { data: subscription } = useSubscription();
} Since the user's subscription really never changed, and to save the perf, we moved the After: export const SubscriptionProvider = ({ children }: SubscriptionProviderProps) => {
// we don't destructure `isLoading` to reduce re-renders since Context API sucks at that
const { data, error } = useSubscription();
const value = useMemo(() => ({ data, error }), [data, error]);
return <SubscriptionProviderContext.Provider value={value}>{children}</SubscriptionProviderContext.Provider>;
};
export const useSubscriptionContext = () => {
const context = useContext(SubscriptionProviderContext);
if (context === defaultValue) {
const error = 'SubscriptionProviderContext used outside of SubscriptionProvider';
if (isDevStage()) {
throw error;
} else {
handleTryCatchError({
error,
context: error,
});
}
}
return context;
};
// and then in our component
const myComponent = () => {
const { data: subscription } = useSubscriptionContext();
} |
Beta Was this translation helpful? Give feedback.
Update: After talking with @TkDodo (thanks for your help), we think the best approach here is to just reduce the number of usages of
useQuery
. There are a bunch of instances of them that we can move up to the top of our application and only have one instance since they don't update very often. In short, usinguseQuery
has a bit of overhead (likeuseSWR
) and if you have a lot of elements with a lot of instances per element, this can seriously add up because of the way observers are added and removed on mount and unmount.To show an example of our change, we previously had a component on our screen with a
useQuery
like this:Before:
// here's one of our reusable hooks that got used in a lot…