diff --git a/src/view/use-drag-handle/use-focus-retainer.js b/src/view/use-drag-handle/use-focus-retainer.js
index cb02823f4a..bae213d4c9 100644
--- a/src/view/use-drag-handle/use-focus-retainer.js
+++ b/src/view/use-drag-handle/use-focus-retainer.js
@@ -27,6 +27,9 @@ export default function useFocusRetainer(args: Args): Result {
isFocusedRef.current = false;
}, []);
+ // This effect handles:
+ // - giving focus on mount
+ // - registering focus on unmount
useIsomorphicLayoutEffect(() => {
// mounting: try to restore focus
const first: Args = lastArgsRef.current;
@@ -63,9 +66,19 @@ export default function useFocusRetainer(args: Args): Result {
};
}, [getDraggableRef]);
- const lastDraggableRef = useRef(getDraggableRef());
+ // will always be null on the first render as nothing has mounted yet
+ const lastDraggableRef = useRef(null);
+ // This effect restores focus to an element when a
+ // ref changes while a component is still mounted.
+ // This can happen when a drag handle is moved into a portal
useIsomorphicLayoutEffect(() => {
+ // this can happen on the first mount - no draggable ref is set
+ // this effect is not handling initial mounting
+ if (!lastDraggableRef.current) {
+ return;
+ }
+
const draggableRef: ?HTMLElement = getDraggableRef();
// Cannot focus on nothing
diff --git a/test/unit/view/drag-handle/focus-management.spec.js b/test/unit/view/drag-handle/focus-management.spec.js
index 865ef2aafc..b1dacbce37 100644
--- a/test/unit/view/drag-handle/focus-management.spec.js
+++ b/test/unit/view/drag-handle/focus-management.spec.js
@@ -2,6 +2,7 @@
import React, { type Node } from 'react';
import invariant from 'tiny-invariant';
import ReactDOM from 'react-dom';
+import { Simulate } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import type { ReactWrapper } from 'enzyme';
import type { DragHandleProps } from '../../../../src/view/use-drag-handle/drag-handle-types';
@@ -12,6 +13,7 @@ import forceUpdate from '../../../utils/force-update';
import AppContext, {
type AppContextValue,
} from '../../../../src/view/context/app-context';
+import createRef from '../../../utils/create-ref';
const body: ?HTMLElement = document.body;
invariant(body, 'Cannot find body');
@@ -385,4 +387,53 @@ describe('Focus retention moving between lists (focus retention between mounts)'
// focus maintained on button
expect(button).toBe(document.activeElement);
});
+
+ it('should not steal focus from an element with auto focus on mount', () => {
+ function App() {
+ const ref = createRef();
+ return (
+
+ true}
+ >
+ {(dragHandleProps: ?DragHandleProps) => (
+
+ Drag me!
+ {/* autoFocus attribute does give focus, but does not trigger onFocus callback */}
+