Skip to content

Commit 0e62dcd

Browse files
authored
Fix react 18 animation flicker (#4435)
* Fix React 18 animation flickering
1 parent 0b384e6 commit 0e62dcd

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

packages/react-aria-components/src/utils.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import {AriaLabelingProps, DOMProps as SharedDOMProps} from '@react-types/shared';
1414
import {filterDOMProps, mergeProps, mergeRefs, useLayoutEffect, useObjectRef} from '@react-aria/utils';
1515
import React, {createContext, CSSProperties, ReactNode, RefCallback, RefObject, useCallback, useContext, useEffect, useRef, useState} from 'react';
16+
import ReactDOM from 'react-dom';
1617

1718
// Override forwardRef types so generics work.
1819
declare function forwardRef<T, P = {}>(
@@ -216,7 +217,7 @@ function useAnimation(ref: RefObject<HTMLElement>, isActive: boolean, onEnd: ()
216217
let onAnimationEnd = (e: AnimationEvent) => {
217218
if (e.target === ref.current) {
218219
element.removeEventListener('animationend', onAnimationEnd);
219-
onEnd();
220+
ReactDOM.flushSync(() => {onEnd();});
220221
}
221222
};
222223

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
import {
3+
Button,
4+
Dialog,
5+
DialogTrigger,
6+
Heading,
7+
Modal,
8+
ModalOverlay
9+
} from 'react-aria-components';
10+
import React from 'react';
11+
import styles from './styles.css';
12+
13+
14+
export default {
15+
title: 'React Aria Components - Animations'
16+
};
17+
18+
export let ModalAnimation = {
19+
render: () => {
20+
return (
21+
<div className="App">
22+
<DialogTrigger>
23+
<Button>Open modal</Button>
24+
<ModalOverlay className={styles['my-overlay']}>
25+
<Modal isDismissable className={styles['my-modal']}>
26+
<Dialog>
27+
{({close}) => (
28+
<>
29+
<Heading>Notice</Heading>
30+
<p>This is a modal with a custom modal overlay.</p>
31+
<Button onPress={close}>Close</Button>
32+
</>
33+
)}
34+
</Dialog>
35+
</Modal>
36+
</ModalOverlay>
37+
</DialogTrigger>
38+
</div>
39+
);
40+
}
41+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.my-overlay {
2+
position: fixed;
3+
inset: 0;
4+
background: rgba(0 0 0 / .5);
5+
6+
&[data-entering] {
7+
animation: fade 5000ms;
8+
}
9+
10+
&[data-exiting] {
11+
animation: fade 5000ms reverse ease-in;
12+
}
13+
}
14+
15+
@keyframes fade {
16+
from {
17+
opacity: 0;
18+
}
19+
20+
to {
21+
opacity: 1;
22+
}
23+
}
24+
25+
26+
.my-modal {
27+
position: fixed;
28+
top: 0;
29+
bottom: 0;
30+
right: 0;
31+
width: 250px;
32+
background: seashell;
33+
outline: none;
34+
padding: 30px;
35+
border-left: 1px solid var(--spectrum-global-color-gray-400);
36+
box-shadow: -8px 0 20px rgba(0 0 0 / 0.1);
37+
38+
&[data-entering] {
39+
animation: slide 5000ms;
40+
}
41+
42+
&[data-exiting] {
43+
animation: slide 5000ms reverse ease-in;
44+
}
45+
}
46+
47+
@keyframes blur {
48+
from {
49+
background: rgba(45 0 0 / 0);
50+
backdrop-filter: blur(0);
51+
}
52+
53+
to {
54+
background: rgba(45 0 0 / .3);
55+
backdrop-filter: blur(10px);
56+
}
57+
}
58+
59+
@keyframes slide {
60+
from {
61+
transform: translateX(100%);
62+
}
63+
64+
to {
65+
transform: translateX(0);
66+
}
67+
}

0 commit comments

Comments
 (0)