react-apollo icon indicating copy to clipboard operation
react-apollo copied to clipboard

useSubscription triggers unnecessary rerenders

Open borremosch opened this issue 5 years ago • 6 comments

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 

borremosch avatar Oct 02 '19 13:10 borremosch

Same issue here

safead avatar Jan 08 '20 19:01 safead

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.

filipmares avatar Mar 12 '20 00:03 filipmares

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..
}

0xdevalias avatar Apr 08 '20 01:04 0xdevalias

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?

icodeforlove avatar May 17 '20 22:05 icodeforlove

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!

tronxdev avatar May 18 '20 17:05 tronxdev

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?

tronxdev avatar May 18 '20 18:05 tronxdev