useActor.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { useRef, useCallback } from 'react';
  2. import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';
  3. import useConstant from './useConstant';
  4. import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';
  5. import { getServiceSnapshot, isInterpreterStateEqual, isService } from './utils';
  6. function identity(a) {
  7. return a;
  8. }
  9. export function isActorWithState(actorRef) {
  10. return 'state' in actorRef;
  11. }
  12. function isDeferredActor(actorRef) {
  13. return 'deferred' in actorRef;
  14. }
  15. function defaultGetSnapshot(actorRef) {
  16. return 'getSnapshot' in actorRef
  17. ? isService(actorRef)
  18. ? getServiceSnapshot(actorRef)
  19. : actorRef.getSnapshot()
  20. : isActorWithState(actorRef)
  21. ? actorRef.state
  22. : undefined;
  23. }
  24. export function useActor(actorRef, getSnapshot) {
  25. if (getSnapshot === void 0) { getSnapshot = defaultGetSnapshot; }
  26. var actorRefRef = useRef(actorRef);
  27. var deferredEventsRef = useRef([]);
  28. var subscribe = useCallback(function (handleStoreChange) {
  29. var unsubscribe = actorRef.subscribe(handleStoreChange).unsubscribe;
  30. return unsubscribe;
  31. }, [actorRef]);
  32. var boundGetSnapshot = useCallback(function () { return getSnapshot(actorRef); }, [actorRef, getSnapshot]);
  33. var isEqual = useCallback(function (prevState, nextState) {
  34. if (isService(actorRef)) {
  35. return isInterpreterStateEqual(actorRef, prevState, nextState);
  36. }
  37. return prevState === nextState;
  38. }, [actorRef]);
  39. var storeSnapshot = useSyncExternalStoreWithSelector(subscribe, boundGetSnapshot, boundGetSnapshot, identity, isEqual);
  40. var send = useConstant(function () { return function () {
  41. var args = [];
  42. for (var _i = 0; _i < arguments.length; _i++) {
  43. args[_i] = arguments[_i];
  44. }
  45. var event = args[0];
  46. if (process.env.NODE_ENV !== 'production' && args.length > 1) {
  47. console.warn("Unexpected payload: ".concat(JSON.stringify(args[1]), ". Only a single event object can be sent to actor send() functions."));
  48. }
  49. var currentActorRef = actorRefRef.current;
  50. // If the previous actor is a deferred actor,
  51. // queue the events so that they can be replayed
  52. // on the non-deferred actor.
  53. if (isDeferredActor(currentActorRef) && currentActorRef.deferred) {
  54. deferredEventsRef.current.push(event);
  55. }
  56. else {
  57. currentActorRef.send(event);
  58. }
  59. }; });
  60. useIsomorphicLayoutEffect(function () {
  61. actorRefRef.current = actorRef;
  62. // Dequeue deferred events from the previous deferred actorRef
  63. while (deferredEventsRef.current.length > 0) {
  64. var deferredEvent = deferredEventsRef.current.shift();
  65. actorRef.send(deferredEvent);
  66. }
  67. }, [actorRef]);
  68. return [storeSnapshot, send];
  69. }