DevTools: useModalDismissSignal bugfix (#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
Brian Vaughn committed
Apr 5, 2021 at 11:09 UTC
a817840ea7d3818c0590cccb9159b13220f4fdb4
1 file changed
+27
-13
packages/react-devtools-shared/src/devtools/views/hooks.js
+27
-13
@@ -207,14 +207,13 @@ export function useModalDismissSignal(
207
return () => {};
208
}
209
210
- const handleDocumentKeyDown = ({key}: any) => {
211
- if (key === 'Escape') {
210
+ const handleDocumentKeyDown = (event: any) => {
211
+ if (event.key === 'Escape') {
212
dismissCallback();
213
}
214
};
215
216
const handleDocumentClick = (event: any) => {
217
- // $FlowFixMe
217
if (
218
modalRef.current !== null &&
219
!modalRef.current.contains(event.target)
@@ -226,18 +225,33 @@ export function useModalDismissSignal(
225
}
226
};
227
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);
245
246
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
+ }
255
};
256
}, [modalRef, dismissCallback, dismissOnClickOutside]);
257
}