@samitouri / QOS-React / commits / a817840ea7

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 }