use-instant-transition.mjs 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. import { frame } from 'motion-dom';
  2. import { useRef, useEffect } from 'react';
  3. import { useInstantLayoutTransition } from '../projection/use-instant-layout-transition.mjs';
  4. import { useForceUpdate } from './use-force-update.mjs';
  5. import { instantAnimationState } from './use-instant-transition-state.mjs';
  6. function useInstantTransition() {
  7. const [forceUpdate, forcedRenderCount] = useForceUpdate();
  8. const startInstantLayoutTransition = useInstantLayoutTransition();
  9. const unlockOnFrameRef = useRef(-1);
  10. useEffect(() => {
  11. /**
  12. * Unblock after two animation frames, otherwise this will unblock too soon.
  13. */
  14. frame.postRender(() => frame.postRender(() => {
  15. /**
  16. * If the callback has been called again after the effect
  17. * triggered this 2 frame delay, don't unblock animations. This
  18. * prevents the previous effect from unblocking the current
  19. * instant transition too soon. This becomes more likely when
  20. * used in conjunction with React.startTransition().
  21. */
  22. if (forcedRenderCount !== unlockOnFrameRef.current)
  23. return;
  24. instantAnimationState.current = false;
  25. }));
  26. }, [forcedRenderCount]);
  27. return (callback) => {
  28. startInstantLayoutTransition(() => {
  29. instantAnimationState.current = true;
  30. forceUpdate();
  31. callback();
  32. unlockOnFrameRef.current = forcedRenderCount + 1;
  33. });
  34. };
  35. }
  36. function disableInstantTransitions() {
  37. instantAnimationState.current = false;
  38. }
  39. export { disableInstantTransitions, useInstantTransition };