diff --git a/packages/shared/invokeGuardedCallbackImpl.js b/packages/shared/invokeGuardedCallbackImpl.js
index 8ba03aa8d9dd6..60e32e0a55287 100644
--- a/packages/shared/invokeGuardedCallbackImpl.js
+++ b/packages/shared/invokeGuardedCallbackImpl.js
@@ -9,7 +9,7 @@
import invariant from 'shared/invariant';
-let invokeGuardedCallbackImpl = function(
+function invokeGuardedCallbackProd(
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(
} 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(
+ 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);
+ }
+ };
}
}