react-apollo
react-apollo copied to clipboard
useSubscription triggers unnecessary rerenders
I am using useSubscription
in one of my components. I do not use any of the return values, because I am using a complicated protocol in my subscription, and I do not want to act on every message I receive. Instead, I am using the onSubscriptionData
callback function. I was surprised to see that even though I only update state on some of the received messages, my component is rerendering continuously. It seems that using useSubscription
will trigger a re-render every time subscription data is received, even if it is discarded.
It would be very useful to have a version of useSubscription
that does not trigger a re-render whenever data is received, and instead let the user decide when to make state changes in onSubscriptionData
. For now, I have had to put my subscription in a sub-component, just to make the superfluous re-rendering computationally cheaper.
I am using @apollo/react-hooks version 3.1.2
.
System:
OS: Linux 5.0 Ubuntu 19.04 (Disco Dingo)
Binaries:
Node: 10.16.0 - /usr/bin/node
Yarn: 1.19.0 - /usr/bin/yarn
npm: 6.10.0 - ~/cubonacci/services/ui/node_modules/.bin/npm
Browsers:
Chrome: 77.0.3865.90
Firefox: 69.0.1
npmPackages:
@apollo/react-components: ^3.1.2 => 3.1.2
@apollo/react-hoc: ^3.1.2 => 3.1.2
@apollo/react-hooks: ^3.1.2 => 3.1.2
apollo-cache-inmemory: ^1.6.3 => 1.6.3
apollo-client: ^2.6.4 => 2.6.4
apollo-link-http: ^1.5.16 => 1.5.16
apollo-link-ws: ^1.0.19 => 1.0.19
Same issue here
My team is also seeing this issue. Causing way too many re-renders. We're going to try the same approach as mentioned by @borremosch.
EDIT: re-reading your original issue, I think my below is actually irrelevant, and the real reason is just that the setResult
is always going to be called, which will cause the re-renders: https://github.com/apollographql/apollo-client/blob/master/src/react/hooks/useSubscription.ts#L19-L23
@borremosch @filipmares I'm not 100% if this will solve your issues, but if you are defining your onSubscriptionData
handler inline, every time the hook is run/processed, you're actually passing an entirely new anonymous function to the hook.
The general way to resolve this is to use React's useCallback
handler:
- https://reactjs.org/docs/hooks-reference.html#usecallback
Digging deeper to confirm the theory, I found the related code at the following:
- https://github.com/apollographql/apollo-client/blob/master/src/react/hooks/useSubscription.ts
- https://github.com/apollographql/apollo-client/blob/master/src/react/data/SubscriptionData.ts#L10-L13
- https://github.com/apollographql/apollo-client/blob/master/src/react/data/OperationData.ts#L28-L36
public setOptions(
newOptions: CommonOptions<TOptions>,
storePrevious: boolean = false
) {
if (storePrevious && !equal(this.options, newOptions)) {
this.previousOptions = this.options;
}
this.options = newOptions;
}
In particular, the !equal(this.options, newOptions)
, which uses:
- https://github.com/benjamn/wryware/blob/master/packages/equality/src/equality.ts#L4-L13
/**
* Performs a deep equality check on two JavaScript values, tolerating cycles.
*/
export function equal(a: any, b: any): boolean {
// ..snip..
}
I am seeing this happening when enabling reconnect.
A rerender is triggered every XX seconds (depends on timeout/keep-alive).
Any idea on how to prevent this rerender?
Today I faced the same issue with useSubscription
. Whenever a subscription is fired, a React component is re-rendered. It isn't mentioned in useSubscription
's API doc. What can I do to prevent the re-rendering?
@0xdevalias , could you elaborate your solution for me? It sounds good, but I'm not sure I follow. Thanks in advance!
My team is also seeing this issue. Causing way too many re-renders. We're going to try the same approach as mentioned by @borremosch.
Hi @filipmares , did you resolve this issue? If yes, could you tell me how to?