framer-motion 8.2.0 → 8.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -1359,12 +1359,12 @@ function useDomEvent(ref, eventName, handler, options) {
1359
1359
  */
1360
1360
  function useFocusGesture({ whileFocus, visualElement, }) {
1361
1361
  const { animationState } = visualElement;
1362
- const onFocus = () => {
1362
+ const onFocus = React.useCallback(() => {
1363
1363
  animationState && animationState.setActive(exports.AnimationType.Focus, true);
1364
- };
1365
- const onBlur = () => {
1364
+ }, [animationState]);
1365
+ const onBlur = React.useCallback(() => {
1366
1366
  animationState && animationState.setActive(exports.AnimationType.Focus, false);
1367
- };
1367
+ }, [animationState]);
1368
1368
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
1369
1369
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
1370
1370
  }
@@ -1463,12 +1463,16 @@ function createHoverEvent(visualElement, isActive, callback) {
1463
1463
  };
1464
1464
  }
1465
1465
  function useHoverGesture({ onHoverStart, onHoverEnd, whileHover, visualElement, }) {
1466
- usePointerEvent(visualElement, "pointerenter", onHoverStart || whileHover
1467
- ? createHoverEvent(visualElement, true, onHoverStart)
1468
- : undefined, { passive: !onHoverStart });
1469
- usePointerEvent(visualElement, "pointerleave", onHoverEnd || whileHover
1470
- ? createHoverEvent(visualElement, false, onHoverEnd)
1471
- : undefined, { passive: !onHoverEnd });
1466
+ usePointerEvent(visualElement, "pointerenter", React.useMemo(() => {
1467
+ return onHoverStart || whileHover
1468
+ ? createHoverEvent(visualElement, true, onHoverStart)
1469
+ : undefined;
1470
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverStart });
1471
+ usePointerEvent(visualElement, "pointerleave", React.useMemo(() => {
1472
+ return onHoverEnd || whileHover
1473
+ ? createHoverEvent(visualElement, false, onHoverEnd)
1474
+ : undefined;
1475
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverEnd });
1472
1476
  }
1473
1477
 
1474
1478
  /**
@@ -1508,7 +1512,7 @@ const pipe = (...transformers) => transformers.reduce(combineFunctions);
1508
1512
  * @param handlers -
1509
1513
  * @internal
1510
1514
  */
1511
- function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, }) {
1515
+ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, ...props }) {
1512
1516
  const hasPressListeners = onTap || onTapStart || onTapCancel || whileTap;
1513
1517
  const isPressing = React.useRef(false);
1514
1518
  const cancelPointerEndListeners = React.useRef(null);
@@ -1516,7 +1520,10 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1516
1520
  * Only set listener to passive if there are no external listeners.
1517
1521
  */
1518
1522
  const eventOptions = {
1519
- passive: !(onTapStart || onTap || onTapCancel || onPointerDown),
1523
+ passive: !(onTapStart ||
1524
+ onTap ||
1525
+ onTapCancel ||
1526
+ props["onPointerDown"]),
1520
1527
  };
1521
1528
  function removePointerEndListener() {
1522
1529
  cancelPointerEndListeners.current && cancelPointerEndListeners.current();
@@ -1545,7 +1552,7 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1545
1552
  return;
1546
1553
  onTapCancel && onTapCancel(event, info);
1547
1554
  }
1548
- function onPointerDown(event, info) {
1555
+ const startPress = React.useCallback((event, info) => {
1549
1556
  removePointerEndListener();
1550
1557
  if (isPressing.current)
1551
1558
  return;
@@ -1557,8 +1564,8 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1557
1564
  visualElement.animationState &&
1558
1565
  visualElement.animationState.setActive(exports.AnimationType.Tap, true);
1559
1566
  onTapStart && onTapStart(event, info);
1560
- }
1561
- usePointerEvent(visualElement, "pointerdown", hasPressListeners ? onPointerDown : undefined, eventOptions);
1567
+ }, [onTapStart, visualElement]);
1568
+ usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
1562
1569
  useUnmountEffect(removePointerEndListener);
1563
1570
  }
1564
1571
 
@@ -2055,7 +2062,7 @@ class MotionValue {
2055
2062
  * This will be replaced by the build step with the latest version number.
2056
2063
  * When MotionValues are provided to motion components, warn if versions are mixed.
2057
2064
  */
2058
- this.version = "8.2.0";
2065
+ this.version = "8.2.3";
2059
2066
  /**
2060
2067
  * Duration, in milliseconds, since last updating frame.
2061
2068
  *
@@ -5911,7 +5918,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5911
5918
  * and warn against mismatches.
5912
5919
  */
5913
5920
  if (process.env.NODE_ENV === "development") {
5914
- warnOnce(nextValue.version === "8.2.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.0 may not work as expected.`);
5921
+ warnOnce(nextValue.version === "8.2.3", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.3 may not work as expected.`);
5915
5922
  }
5916
5923
  }
5917
5924
  else if (isMotionValue(prevValue)) {
@@ -9439,11 +9446,6 @@ function animationControls() {
9439
9446
  * Track whether the host component has mounted.
9440
9447
  */
9441
9448
  let hasMounted = false;
9442
- /**
9443
- * Pending animations that are started before a component is mounted.
9444
- * TODO: Remove this as animations should only run in effects
9445
- */
9446
- const pendingAnimations = [];
9447
9449
  /**
9448
9450
  * A collection of linked component animation controls.
9449
9451
  */
@@ -9454,29 +9456,14 @@ function animationControls() {
9454
9456
  return () => void subscribers.delete(visualElement);
9455
9457
  },
9456
9458
  start(definition, transitionOverride) {
9457
- /**
9458
- * TODO: We only perform this hasMounted check because in Framer we used to
9459
- * encourage the ability to start an animation within the render phase. This
9460
- * isn't behaviour concurrent-safe so when we make Framer concurrent-safe
9461
- * we can ditch this.
9462
- */
9463
- if (hasMounted) {
9464
- const animations = [];
9465
- subscribers.forEach((visualElement) => {
9466
- animations.push(animateVisualElement(visualElement, definition, {
9467
- transitionOverride,
9468
- }));
9469
- });
9470
- return Promise.all(animations);
9471
- }
9472
- else {
9473
- return new Promise((resolve) => {
9474
- pendingAnimations.push({
9475
- animation: [definition, transitionOverride],
9476
- resolve,
9477
- });
9478
- });
9479
- }
9459
+ heyListen.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
9460
+ const animations = [];
9461
+ subscribers.forEach((visualElement) => {
9462
+ animations.push(animateVisualElement(visualElement, definition, {
9463
+ transitionOverride,
9464
+ }));
9465
+ });
9466
+ return Promise.all(animations);
9480
9467
  },
9481
9468
  set(definition) {
9482
9469
  heyListen.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
@@ -9491,9 +9478,6 @@ function animationControls() {
9491
9478
  },
9492
9479
  mount() {
9493
9480
  hasMounted = true;
9494
- pendingAnimations.forEach(({ animation, resolve }) => {
9495
- controls.start(...animation).then(resolve);
9496
- });
9497
9481
  return () => {
9498
9482
  hasMounted = false;
9499
9483
  controls.stop();
@@ -9534,7 +9518,7 @@ function animationControls() {
9534
9518
  */
9535
9519
  function useAnimationControls() {
9536
9520
  const controls = useConstant(animationControls);
9537
- React.useEffect(controls.mount, []);
9521
+ useIsomorphicLayoutEffect(controls.mount, []);
9538
9522
  return controls;
9539
9523
  }
9540
9524
  const useAnimation = useAnimationControls;
@@ -1,5 +1,5 @@
1
1
  import { invariant } from 'hey-listen';
2
- import { stopAnimation, animateVisualElement } from '../../render/utils/animation.mjs';
2
+ import { animateVisualElement, stopAnimation } from '../../render/utils/animation.mjs';
3
3
  import { setValues } from '../../render/utils/setters.mjs';
4
4
 
5
5
  /**
@@ -10,11 +10,6 @@ function animationControls() {
10
10
  * Track whether the host component has mounted.
11
11
  */
12
12
  let hasMounted = false;
13
- /**
14
- * Pending animations that are started before a component is mounted.
15
- * TODO: Remove this as animations should only run in effects
16
- */
17
- const pendingAnimations = [];
18
13
  /**
19
14
  * A collection of linked component animation controls.
20
15
  */
@@ -25,29 +20,14 @@ function animationControls() {
25
20
  return () => void subscribers.delete(visualElement);
26
21
  },
27
22
  start(definition, transitionOverride) {
28
- /**
29
- * TODO: We only perform this hasMounted check because in Framer we used to
30
- * encourage the ability to start an animation within the render phase. This
31
- * isn't behaviour concurrent-safe so when we make Framer concurrent-safe
32
- * we can ditch this.
33
- */
34
- if (hasMounted) {
35
- const animations = [];
36
- subscribers.forEach((visualElement) => {
37
- animations.push(animateVisualElement(visualElement, definition, {
38
- transitionOverride,
39
- }));
40
- });
41
- return Promise.all(animations);
42
- }
43
- else {
44
- return new Promise((resolve) => {
45
- pendingAnimations.push({
46
- animation: [definition, transitionOverride],
47
- resolve,
48
- });
49
- });
50
- }
23
+ invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
24
+ const animations = [];
25
+ subscribers.forEach((visualElement) => {
26
+ animations.push(animateVisualElement(visualElement, definition, {
27
+ transitionOverride,
28
+ }));
29
+ });
30
+ return Promise.all(animations);
51
31
  },
52
32
  set(definition) {
53
33
  invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
@@ -62,9 +42,6 @@ function animationControls() {
62
42
  },
63
43
  mount() {
64
44
  hasMounted = true;
65
- pendingAnimations.forEach(({ animation, resolve }) => {
66
- controls.start(...animation).then(resolve);
67
- });
68
45
  return () => {
69
46
  hasMounted = false;
70
47
  controls.stop();
@@ -1,6 +1,6 @@
1
1
  import { animationControls } from './animation-controls.mjs';
2
- import { useEffect } from 'react';
3
2
  import { useConstant } from '../../utils/use-constant.mjs';
3
+ import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
4
4
 
5
5
  /**
6
6
  * Creates `AnimationControls`, which can be used to manually start, stop
@@ -33,7 +33,7 @@ import { useConstant } from '../../utils/use-constant.mjs';
33
33
  */
34
34
  function useAnimationControls() {
35
35
  const controls = useConstant(animationControls);
36
- useEffect(controls.mount, []);
36
+ useIsomorphicLayoutEffect(controls.mount, []);
37
37
  return controls;
38
38
  }
39
39
  const useAnimation = useAnimationControls;
@@ -1,5 +1,6 @@
1
1
  import { AnimationType } from '../render/utils/types.mjs';
2
2
  import { useDomEvent } from '../events/use-dom-event.mjs';
3
+ import { useCallback } from 'react';
3
4
 
4
5
  /**
5
6
  *
@@ -9,12 +10,12 @@ import { useDomEvent } from '../events/use-dom-event.mjs';
9
10
  */
10
11
  function useFocusGesture({ whileFocus, visualElement, }) {
11
12
  const { animationState } = visualElement;
12
- const onFocus = () => {
13
+ const onFocus = useCallback(() => {
13
14
  animationState && animationState.setActive(AnimationType.Focus, true);
14
- };
15
- const onBlur = () => {
15
+ }, [animationState]);
16
+ const onBlur = useCallback(() => {
16
17
  animationState && animationState.setActive(AnimationType.Focus, false);
17
- };
18
+ }, [animationState]);
18
19
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
19
20
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
20
21
  }
@@ -1,6 +1,7 @@
1
1
  import { AnimationType } from '../render/utils/types.mjs';
2
2
  import { usePointerEvent } from '../events/use-pointer-event.mjs';
3
3
  import { isDragActive } from './drag/utils/lock.mjs';
4
+ import { useMemo } from 'react';
4
5
 
5
6
  function isMouseEvent(event) {
6
7
  return event.type !== "pen" && event.type !== "touch";
@@ -19,12 +20,16 @@ function createHoverEvent(visualElement, isActive, callback) {
19
20
  };
20
21
  }
21
22
  function useHoverGesture({ onHoverStart, onHoverEnd, whileHover, visualElement, }) {
22
- usePointerEvent(visualElement, "pointerenter", onHoverStart || whileHover
23
- ? createHoverEvent(visualElement, true, onHoverStart)
24
- : undefined, { passive: !onHoverStart });
25
- usePointerEvent(visualElement, "pointerleave", onHoverEnd || whileHover
26
- ? createHoverEvent(visualElement, false, onHoverEnd)
27
- : undefined, { passive: !onHoverEnd });
23
+ usePointerEvent(visualElement, "pointerenter", useMemo(() => {
24
+ return onHoverStart || whileHover
25
+ ? createHoverEvent(visualElement, true, onHoverStart)
26
+ : undefined;
27
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverStart });
28
+ usePointerEvent(visualElement, "pointerleave", useMemo(() => {
29
+ return onHoverEnd || whileHover
30
+ ? createHoverEvent(visualElement, false, onHoverEnd)
31
+ : undefined;
32
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverEnd });
28
33
  }
29
34
 
30
35
  export { useHoverGesture };
@@ -1,6 +1,6 @@
1
- import { useRef } from 'react';
1
+ import { useRef, useCallback } from 'react';
2
2
  import { isNodeOrChild } from './utils/is-node-or-child.mjs';
3
- import { usePointerEvent, addPointerEvent } from '../events/use-pointer-event.mjs';
3
+ import { addPointerEvent, usePointerEvent } from '../events/use-pointer-event.mjs';
4
4
  import { useUnmountEffect } from '../utils/use-unmount-effect.mjs';
5
5
  import { AnimationType } from '../render/utils/types.mjs';
6
6
  import { isDragActive } from './drag/utils/lock.mjs';
@@ -10,7 +10,7 @@ import { pipe } from '../utils/pipe.mjs';
10
10
  * @param handlers -
11
11
  * @internal
12
12
  */
13
- function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, }) {
13
+ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, ...props }) {
14
14
  const hasPressListeners = onTap || onTapStart || onTapCancel || whileTap;
15
15
  const isPressing = useRef(false);
16
16
  const cancelPointerEndListeners = useRef(null);
@@ -18,7 +18,10 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
18
18
  * Only set listener to passive if there are no external listeners.
19
19
  */
20
20
  const eventOptions = {
21
- passive: !(onTapStart || onTap || onTapCancel || onPointerDown),
21
+ passive: !(onTapStart ||
22
+ onTap ||
23
+ onTapCancel ||
24
+ props["onPointerDown"]),
22
25
  };
23
26
  function removePointerEndListener() {
24
27
  cancelPointerEndListeners.current && cancelPointerEndListeners.current();
@@ -47,7 +50,7 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
47
50
  return;
48
51
  onTapCancel && onTapCancel(event, info);
49
52
  }
50
- function onPointerDown(event, info) {
53
+ const startPress = useCallback((event, info) => {
51
54
  removePointerEndListener();
52
55
  if (isPressing.current)
53
56
  return;
@@ -59,8 +62,8 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
59
62
  visualElement.animationState &&
60
63
  visualElement.animationState.setActive(AnimationType.Tap, true);
61
64
  onTapStart && onTapStart(event, info);
62
- }
63
- usePointerEvent(visualElement, "pointerdown", hasPressListeners ? onPointerDown : undefined, eventOptions);
65
+ }, [onTapStart, visualElement]);
66
+ usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
64
67
  useUnmountEffect(removePointerEndListener);
65
68
  }
66
69
 
@@ -22,7 +22,7 @@ function updateMotionValuesFromProps(element, next, prev) {
22
22
  * and warn against mismatches.
23
23
  */
24
24
  if (process.env.NODE_ENV === "development") {
25
- warnOnce(nextValue.version === "8.2.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.0 may not work as expected.`);
25
+ warnOnce(nextValue.version === "8.2.3", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.3 may not work as expected.`);
26
26
  }
27
27
  }
28
28
  else if (isMotionValue(prevValue)) {
@@ -25,7 +25,7 @@ class MotionValue {
25
25
  * This will be replaced by the build step with the latest version number.
26
26
  * When MotionValues are provided to motion components, warn if versions are mixed.
27
27
  */
28
- this.version = "8.2.0";
28
+ this.version = "8.2.3";
29
29
  /**
30
30
  * Duration, in milliseconds, since last updating frame.
31
31
  *
@@ -1357,12 +1357,12 @@
1357
1357
  */
1358
1358
  function useFocusGesture({ whileFocus, visualElement, }) {
1359
1359
  const { animationState } = visualElement;
1360
- const onFocus = () => {
1360
+ const onFocus = React.useCallback(() => {
1361
1361
  animationState && animationState.setActive(exports.AnimationType.Focus, true);
1362
- };
1363
- const onBlur = () => {
1362
+ }, [animationState]);
1363
+ const onBlur = React.useCallback(() => {
1364
1364
  animationState && animationState.setActive(exports.AnimationType.Focus, false);
1365
- };
1365
+ }, [animationState]);
1366
1366
  useDomEvent(visualElement, "focus", whileFocus ? onFocus : undefined);
1367
1367
  useDomEvent(visualElement, "blur", whileFocus ? onBlur : undefined);
1368
1368
  }
@@ -1461,12 +1461,16 @@
1461
1461
  };
1462
1462
  }
1463
1463
  function useHoverGesture({ onHoverStart, onHoverEnd, whileHover, visualElement, }) {
1464
- usePointerEvent(visualElement, "pointerenter", onHoverStart || whileHover
1465
- ? createHoverEvent(visualElement, true, onHoverStart)
1466
- : undefined, { passive: !onHoverStart });
1467
- usePointerEvent(visualElement, "pointerleave", onHoverEnd || whileHover
1468
- ? createHoverEvent(visualElement, false, onHoverEnd)
1469
- : undefined, { passive: !onHoverEnd });
1464
+ usePointerEvent(visualElement, "pointerenter", React.useMemo(() => {
1465
+ return onHoverStart || whileHover
1466
+ ? createHoverEvent(visualElement, true, onHoverStart)
1467
+ : undefined;
1468
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverStart });
1469
+ usePointerEvent(visualElement, "pointerleave", React.useMemo(() => {
1470
+ return onHoverEnd || whileHover
1471
+ ? createHoverEvent(visualElement, false, onHoverEnd)
1472
+ : undefined;
1473
+ }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverEnd });
1470
1474
  }
1471
1475
 
1472
1476
  /**
@@ -1506,7 +1510,7 @@
1506
1510
  * @param handlers -
1507
1511
  * @internal
1508
1512
  */
1509
- function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, }) {
1513
+ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement, ...props }) {
1510
1514
  const hasPressListeners = onTap || onTapStart || onTapCancel || whileTap;
1511
1515
  const isPressing = React.useRef(false);
1512
1516
  const cancelPointerEndListeners = React.useRef(null);
@@ -1514,7 +1518,10 @@
1514
1518
  * Only set listener to passive if there are no external listeners.
1515
1519
  */
1516
1520
  const eventOptions = {
1517
- passive: !(onTapStart || onTap || onTapCancel || onPointerDown),
1521
+ passive: !(onTapStart ||
1522
+ onTap ||
1523
+ onTapCancel ||
1524
+ props["onPointerDown"]),
1518
1525
  };
1519
1526
  function removePointerEndListener() {
1520
1527
  cancelPointerEndListeners.current && cancelPointerEndListeners.current();
@@ -1543,7 +1550,7 @@
1543
1550
  return;
1544
1551
  onTapCancel && onTapCancel(event, info);
1545
1552
  }
1546
- function onPointerDown(event, info) {
1553
+ const startPress = React.useCallback((event, info) => {
1547
1554
  removePointerEndListener();
1548
1555
  if (isPressing.current)
1549
1556
  return;
@@ -1555,8 +1562,8 @@
1555
1562
  visualElement.animationState &&
1556
1563
  visualElement.animationState.setActive(exports.AnimationType.Tap, true);
1557
1564
  onTapStart && onTapStart(event, info);
1558
- }
1559
- usePointerEvent(visualElement, "pointerdown", hasPressListeners ? onPointerDown : undefined, eventOptions);
1565
+ }, [onTapStart, visualElement]);
1566
+ usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
1560
1567
  useUnmountEffect(removePointerEndListener);
1561
1568
  }
1562
1569
 
@@ -2053,7 +2060,7 @@
2053
2060
  * This will be replaced by the build step with the latest version number.
2054
2061
  * When MotionValues are provided to motion components, warn if versions are mixed.
2055
2062
  */
2056
- this.version = "8.2.0";
2063
+ this.version = "8.2.3";
2057
2064
  /**
2058
2065
  * Duration, in milliseconds, since last updating frame.
2059
2066
  *
@@ -5924,7 +5931,7 @@
5924
5931
  * and warn against mismatches.
5925
5932
  */
5926
5933
  {
5927
- warnOnce(nextValue.version === "8.2.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.0 may not work as expected.`);
5934
+ warnOnce(nextValue.version === "8.2.3", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.2.3 may not work as expected.`);
5928
5935
  }
5929
5936
  }
5930
5937
  else if (isMotionValue(prevValue)) {
@@ -10058,11 +10065,6 @@
10058
10065
  * Track whether the host component has mounted.
10059
10066
  */
10060
10067
  let hasMounted = false;
10061
- /**
10062
- * Pending animations that are started before a component is mounted.
10063
- * TODO: Remove this as animations should only run in effects
10064
- */
10065
- const pendingAnimations = [];
10066
10068
  /**
10067
10069
  * A collection of linked component animation controls.
10068
10070
  */
@@ -10073,29 +10075,14 @@
10073
10075
  return () => void subscribers.delete(visualElement);
10074
10076
  },
10075
10077
  start(definition, transitionOverride) {
10076
- /**
10077
- * TODO: We only perform this hasMounted check because in Framer we used to
10078
- * encourage the ability to start an animation within the render phase. This
10079
- * isn't behaviour concurrent-safe so when we make Framer concurrent-safe
10080
- * we can ditch this.
10081
- */
10082
- if (hasMounted) {
10083
- const animations = [];
10084
- subscribers.forEach((visualElement) => {
10085
- animations.push(animateVisualElement(visualElement, definition, {
10086
- transitionOverride,
10087
- }));
10088
- });
10089
- return Promise.all(animations);
10090
- }
10091
- else {
10092
- return new Promise((resolve) => {
10093
- pendingAnimations.push({
10094
- animation: [definition, transitionOverride],
10095
- resolve,
10096
- });
10097
- });
10098
- }
10078
+ invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
10079
+ const animations = [];
10080
+ subscribers.forEach((visualElement) => {
10081
+ animations.push(animateVisualElement(visualElement, definition, {
10082
+ transitionOverride,
10083
+ }));
10084
+ });
10085
+ return Promise.all(animations);
10099
10086
  },
10100
10087
  set(definition) {
10101
10088
  invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
@@ -10110,9 +10097,6 @@
10110
10097
  },
10111
10098
  mount() {
10112
10099
  hasMounted = true;
10113
- pendingAnimations.forEach(({ animation, resolve }) => {
10114
- controls.start(...animation).then(resolve);
10115
- });
10116
10100
  return () => {
10117
10101
  hasMounted = false;
10118
10102
  controls.stop();
@@ -10153,7 +10137,7 @@
10153
10137
  */
10154
10138
  function useAnimationControls() {
10155
10139
  const controls = useConstant(animationControls);
10156
- React.useEffect(controls.mount, []);
10140
+ useIsomorphicLayoutEffect(controls.mount, []);
10157
10141
  return controls;
10158
10142
  }
10159
10143
  const useAnimation = useAnimationControls;