re-rxjs / react-rxjs

React bindings for RxJS
https://react-rxjs.org
MIT License
542 stars 17 forks source link

hook returned by bind(functionAcceptingArrayReturningAsyncObservable$) should not cause infinite render loop #289

Closed bruceharris closed 1 year ago

bruceharris commented 1 year ago

Observed Behavior

Given

  1. a function getAsyncObservableFromArray$(words: string[]) that returns an asynchronous observable
  2. a use... hook that is derived from aforementioned function
  3. a React component that invokes the aforementioned hook function

... the component causes an infinite render loop.

Expected Behavior

There should be no infinite render loop, the behavior should be no different from a similar scenario with

  1. a function getObservableFromArray$(words: string[]) that returns a synchronous observable
  2. a function getAsyncObservableFromScalar$(word: string) that returns an asynchronous observable

Steps to Reproduce

Uncomment line 35 in App.tsx in this code sandbox

voliva commented 1 year ago

Hey Bruce, thank you for your report!

However, this is intended behaviour. The parameters on a bind are meant to give you an observable for one specific instance, so they need to be identifying - and ReactRxJS uses referential equality.

The problem here is that because on every render you're creating a new array, bind will understand you have just switched to another instance of the observable. So it will unsubscribe from the previous one, call the factory function again (passing in the new array), and it will subscribe to the new observable returned by that function.

Because it's asynchronous, the hook will trigger suspense and when the value comes back, it will set the state. Which causes a re-render, which starts the cycle over again (a new array is created, bind understands it's a new instance, etc.)

The way to work around that depends a lot on your specific case, but usually it's moving the parameter you want to pass to the observable into another state observable, and then referencing that from your bind.

bruceharris commented 1 year ago

Thanks very much @voliva for the excellent explanation, much appreciated!