useLegacyStore.tsx 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import {useEffect, useRef, useState} from 'react';
  2. import {Store} from 'reflux';
  3. import {SafeRefluxStore} from '../utils/makeSafeRefluxStore';
  4. import {CommonStoreDefinition} from './types';
  5. interface UnsafeStore extends Store, CommonStoreDefinition<any> {}
  6. interface SafeStore extends SafeRefluxStore, CommonStoreDefinition<any> {}
  7. type LegacyStoreShape = UnsafeStore | SafeStore;
  8. /**
  9. * This wrapper exists because we have many old-style enzyme tests that trigger
  10. * updates to stores without being wrapped in act.
  11. *
  12. * Wrting tests with React Testing Library typically circumvents the need for
  13. * this. See [0].
  14. *
  15. * [0]: https://javascript.plainenglish.io/you-probably-dont-need-act-in-your-react-tests-2a0bcd2ad65c
  16. */
  17. if (typeof window !== 'undefined') {
  18. window._legacyStoreHookUpdate = update => update();
  19. }
  20. /**
  21. * Returns the state of a reflux store. Automatically unsubscribes when destroyed
  22. *
  23. * ```
  24. * const teams = useLegacyStore(TeamStore);
  25. * ```
  26. */
  27. export function useLegacyStore<T extends LegacyStoreShape>(
  28. store: T
  29. ): ReturnType<T['getState']> {
  30. const [state, setState] = useState(store.getState());
  31. // Not all stores emit the new state, call get on change
  32. const callback = () =>
  33. window !== 'undefined'
  34. ? window._legacyStoreHookUpdate(() => setState(store.getState()))
  35. : null;
  36. // If we setup the listener in useEffect, there is a small race condition
  37. // where the store may emit an event before we're listening (since useEffect
  38. // fires AFTER rendering). Avoid this by ensuring we start listening
  39. // *immediately* after we initialize the useState.
  40. const unlisten = useRef<Function>();
  41. if (unlisten.current === undefined) {
  42. unlisten.current = store.listen(callback, undefined);
  43. }
  44. useEffect(() => {
  45. return () => void unlisten.current?.();
  46. }, []);
  47. return state;
  48. }