Fix development mode hang when iframe is removed (#19220)

* Fix development mode hang when iframe is removed

* Also fix #16734
This commit is contained in:
Dan Abramov
2020-07-01 15:33:29 +01:00
committed by GitHub
parent 8bff8987e5
commit f918b0eb41
+33 -11
View File
@@ -9,7 +9,7 @@
import invariant from 'shared/invariant';
let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
function invokeGuardedCallbackProd<A, B, C, D, E, F, Context>(
name: string | null,
func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
context: Context,
@@ -26,7 +26,9 @@ let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
} catch (error) {
this.onError(error);
}
};
}
let invokeGuardedCallbackImpl = invokeGuardedCallbackProd;
if (__DEV__) {
// In DEV mode, we swap out invokeGuardedCallback for a special version
@@ -58,7 +60,15 @@ if (__DEV__) {
) {
const fakeNode = document.createElement('react');
const invokeGuardedCallbackDev = function<A, B, C, D, E, F, Context>(
invokeGuardedCallbackImpl = function invokeGuardedCallbackDev<
A,
B,
C,
D,
E,
F,
Context,
>(
name: string | null,
func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
context: Context,
@@ -85,6 +95,7 @@ if (__DEV__) {
);
const evt = document.createEvent('Event');
let didCall = false;
// Keeps track of whether the user-provided callback threw an error. We
// set this to true at the beginning, then set it to false right after
// calling the function. If the function errors, `didError` will never be
@@ -105,11 +116,7 @@ if (__DEV__) {
'event',
);
// Create an event handler for our fake event. We will synchronously
// dispatch our fake event using `dispatchEvent`. Inside the handler, we
// call the user-provided callback.
const funcArgs = Array.prototype.slice.call(arguments, 3);
function callCallback() {
function restoreAfterDispatch() {
// We immediately remove the callback from event listeners so that
// nested `invokeGuardedCallback` calls do not clash. Otherwise, a
// nested call would trigger the fake event handlers of any call higher
@@ -126,7 +133,15 @@ if (__DEV__) {
) {
window.event = windowEvent;
}
}
// Create an event handler for our fake event. We will synchronously
// dispatch our fake event using `dispatchEvent`. Inside the handler, we
// call the user-provided callback.
const funcArgs = Array.prototype.slice.call(arguments, 3);
function callCallback() {
didCall = true;
restoreAfterDispatch();
func.apply(context, funcArgs);
didError = false;
}
@@ -183,7 +198,7 @@ if (__DEV__) {
Object.defineProperty(window, 'event', windowEventDescriptor);
}
if (didError) {
if (didCall && didError) {
if (!didSetError) {
// The callback errored, but the error event never fired.
error = new Error(
@@ -208,9 +223,16 @@ if (__DEV__) {
// Remove our event listeners
window.removeEventListener('error', handleWindowError);
};
invokeGuardedCallbackImpl = invokeGuardedCallbackDev;
if (!didCall) {
// Something went really wrong, and our event was not dispatched.
// https://github.com/facebook/react/issues/16734
// https://github.com/facebook/react/issues/16585
// Fall back to the production implementation.
restoreAfterDispatch();
return invokeGuardedCallbackProd.apply(this, arguments);
}
};
}
}