Skip to content

Commit ad6490c

Browse files
Brian Vaughnacdlite
authored andcommitted
DevTools: useModalDismissSignal bugfix (facebook#21173)
* DevTools: useModalDismissSignal bugfix Make useModalDismissSignal's manually added click/keyboard events more robust to sync flushed passive effects. (Don't let the same click event that shows a modal dialog also dismiss it.) * Replaced event.timeStamp check with setTimeout
1 parent 5557f7c commit ad6490c

File tree

1 file changed

+27
-13
lines changed
  • packages/react-devtools-shared/src/devtools/views

1 file changed

+27
-13
lines changed

packages/react-devtools-shared/src/devtools/views/hooks.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,13 @@ export function useModalDismissSignal(
207207
return () => {};
208208
}
209209

210-
const handleDocumentKeyDown = ({key}: any) => {
211-
if (key === 'Escape') {
210+
const handleDocumentKeyDown = (event: any) => {
211+
if (event.key === 'Escape') {
212212
dismissCallback();
213213
}
214214
};
215215

216216
const handleDocumentClick = (event: any) => {
217-
// $FlowFixMe
218217
if (
219218
modalRef.current !== null &&
220219
!modalRef.current.contains(event.target)
@@ -226,18 +225,33 @@ export function useModalDismissSignal(
226225
}
227226
};
228227

229-
// It's important to listen to the ownerDocument to support the browser extension.
230-
// Here we use portals to render individual tabs (e.g. Profiler),
231-
// and the root document might belong to a different window.
232-
const ownerDocument = modalRef.current.ownerDocument;
233-
ownerDocument.addEventListener('keydown', handleDocumentKeyDown);
234-
if (dismissOnClickOutside) {
235-
ownerDocument.addEventListener('click', handleDocumentClick);
236-
}
228+
let ownerDocument = null;
229+
230+
// Delay until after the current call stack is empty,
231+
// in case this effect is being run while an event is currently bubbling.
232+
// In that case, we don't want to listen to the pre-existing event.
233+
let timeoutID = setTimeout(() => {
234+
timeoutID = null;
235+
236+
// It's important to listen to the ownerDocument to support the browser extension.
237+
// Here we use portals to render individual tabs (e.g. Profiler),
238+
// and the root document might belong to a different window.
239+
ownerDocument = ((modalRef.current: any): HTMLDivElement).ownerDocument;
240+
ownerDocument.addEventListener('keydown', handleDocumentKeyDown);
241+
if (dismissOnClickOutside) {
242+
ownerDocument.addEventListener('click', handleDocumentClick);
243+
}
244+
}, 0);
237245

238246
return () => {
239-
ownerDocument.removeEventListener('keydown', handleDocumentKeyDown);
240-
ownerDocument.removeEventListener('click', handleDocumentClick);
247+
if (timeoutID !== null) {
248+
clearTimeout(timeoutID);
249+
}
250+
251+
if (ownerDocument !== null) {
252+
ownerDocument.removeEventListener('keydown', handleDocumentKeyDown);
253+
ownerDocument.removeEventListener('click', handleDocumentClick);
254+
}
241255
};
242256
}, [modalRef, dismissCallback, dismissOnClickOutside]);
243257
}

0 commit comments

Comments
 (0)