crimx/observable-hooks

[Bug]: `useObservable*` hooks does not work when observed value is a `Map` instance

nichita-pasecinic opened this issue · 3 comments

In case a JS Map is used as value for BehaviorSubject the value provided from useObservable* hooks will be stale.

Try:

const subject = new BehaviorSubject(new Map());

// subscription outside React (works as expected - emitted 4 times)
subject.subscribe((map) => {
  console.log('map is:', map.keys());
});

setTimeout(() => {
  subject.next(subject.value.set(1, 1));
  setTimeout(() => {
    subject.next(subject.value.set(2, 2));
    setTimeout(() => {
      subject.next(subject.value.set(3, 3));
    }, 1000);
  }, 1000);
}, 1000);

// define react component
const Component = () => {
  const map = useObservableEagerState(subject);
  console.log('map keys: ', map.keys()); // <-- it will NOT trigger rerender 4 times
  return <></>
}

useObservableState and useObservableEagerState follow the React useState convention. If you need to trigger rendering without changing the value, use useSubscription + useRef instead.

@crimx thanks (I thought that calling .next on subject will trigger a re-render), how would I do it with useSubscription + useRef ?

I'd really appreciate if you helped me.

Calling .next will trigger the observer callback. useObservableState and useObservableEagerState consume the value as React state, so no re-rendering is triggered.

To force update, you can use a very simple hook like useUpdate

import { useUpdate } from "react-use";
import { useSubscription } from "observable-hooks";

const subject = new BehaviorSubject(new Map());

export const Comp = () => {
  const update = useUpdate();
  useSubscription(subject, update);
  console.log('map keys: ', subject.keys());
}