diff --git a/packages/react-native-web/src/exports/Modal/ModalAnimation.js b/packages/react-native-web/src/exports/Modal/ModalAnimation.js
index 1ef6a422..b053d353 100644
--- a/packages/react-native-web/src/exports/Modal/ModalAnimation.js
+++ b/packages/react-native-web/src/exports/Modal/ModalAnimation.js
@@ -37,6 +37,7 @@ function ModalAnimation(props: ModalAnimationProps): React.Node {
const [isRendering, setIsRendering] = React.useState(false);
const wasVisible = React.useRef(false);
+ const wasRendering = React.useRef(false);
const isAnimated = animationType && animationType !== 'none';
@@ -54,14 +55,18 @@ function ModalAnimation(props: ModalAnimationProps): React.Node {
}
} else {
setIsRendering(false);
- if (onDismiss) {
- onDismiss();
- }
}
},
- [onDismiss, onShow, visible]
+ [onShow, visible]
);
+ React.useEffect(() => {
+ if (wasRendering.current && !isRendering && onDismiss) {
+ onDismiss();
+ }
+ wasRendering.current = isRendering;
+ }, [isRendering, onDismiss]);
+
React.useEffect(() => {
if (visible) {
setIsRendering(true);
diff --git a/packages/react-native-web/src/exports/Modal/__tests__/index-test.js b/packages/react-native-web/src/exports/Modal/__tests__/index-test.js
index 78f5d94f..9e82fcc5 100644
--- a/packages/react-native-web/src/exports/Modal/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/Modal/__tests__/index-test.js
@@ -309,6 +309,32 @@ describe('components/Modal', () => {
expect(document.activeElement).toBe(insideElement);
});
+ test('focus is not trapped after closing modal', () => {
+ const { rerender } = render(
+ <>
+
+ Outside
+
+
+ >
+ );
+
+ const outsideElement = document.querySelector('[data-testid="outside"]');
+ const onDismissCallback = jest.fn(() => outsideElement.focus());
+
+ rerender(
+ <>
+
+ Outside
+
+
+ >
+ );
+
+ expect(onDismissCallback).toBeCalledTimes(1);
+ expect(document.activeElement).toBe(outsideElement);
+ });
+
test('focus is brought back to the element that triggered modal after closing', () => {
const { rerender } = render(
<>