Skip to content

Commit f918b0e

Browse files
authored
Fix development mode hang when iframe is removed (facebook#19220)
* Fix development mode hang when iframe is removed * Also fix facebook#16734
1 parent 8bff898 commit f918b0e

File tree

1 file changed

+33
-11
lines changed

1 file changed

+33
-11
lines changed

packages/shared/invokeGuardedCallbackImpl.js

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import invariant from 'shared/invariant';
1111

12-
let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
12+
function invokeGuardedCallbackProd<A, B, C, D, E, F, Context>(
1313
name: string | null,
1414
func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
1515
context: Context,
@@ -26,7 +26,9 @@ let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
2626
} catch (error) {
2727
this.onError(error);
2828
}
29-
};
29+
}
30+
31+
let invokeGuardedCallbackImpl = invokeGuardedCallbackProd;
3032

3133
if (__DEV__) {
3234
// In DEV mode, we swap out invokeGuardedCallback for a special version
@@ -58,7 +60,15 @@ if (__DEV__) {
5860
) {
5961
const fakeNode = document.createElement('react');
6062

61-
const invokeGuardedCallbackDev = function<A, B, C, D, E, F, Context>(
63+
invokeGuardedCallbackImpl = function invokeGuardedCallbackDev<
64+
A,
65+
B,
66+
C,
67+
D,
68+
E,
69+
F,
70+
Context,
71+
>(
6272
name: string | null,
6373
func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
6474
context: Context,
@@ -85,6 +95,7 @@ if (__DEV__) {
8595
);
8696
const evt = document.createEvent('Event');
8797

98+
let didCall = false;
8899
// Keeps track of whether the user-provided callback threw an error. We
89100
// set this to true at the beginning, then set it to false right after
90101
// calling the function. If the function errors, `didError` will never be
@@ -105,11 +116,7 @@ if (__DEV__) {
105116
'event',
106117
);
107118

108-
// Create an event handler for our fake event. We will synchronously
109-
// dispatch our fake event using `dispatchEvent`. Inside the handler, we
110-
// call the user-provided callback.
111-
const funcArgs = Array.prototype.slice.call(arguments, 3);
112-
function callCallback() {
119+
function restoreAfterDispatch() {
113120
// We immediately remove the callback from event listeners so that
114121
// nested `invokeGuardedCallback` calls do not clash. Otherwise, a
115122
// nested call would trigger the fake event handlers of any call higher
@@ -126,7 +133,15 @@ if (__DEV__) {
126133
) {
127134
window.event = windowEvent;
128135
}
136+
}
129137

138+
// Create an event handler for our fake event. We will synchronously
139+
// dispatch our fake event using `dispatchEvent`. Inside the handler, we
140+
// call the user-provided callback.
141+
const funcArgs = Array.prototype.slice.call(arguments, 3);
142+
function callCallback() {
143+
didCall = true;
144+
restoreAfterDispatch();
130145
func.apply(context, funcArgs);
131146
didError = false;
132147
}
@@ -183,7 +198,7 @@ if (__DEV__) {
183198
Object.defineProperty(window, 'event', windowEventDescriptor);
184199
}
185200

186-
if (didError) {
201+
if (didCall && didError) {
187202
if (!didSetError) {
188203
// The callback errored, but the error event never fired.
189204
error = new Error(
@@ -208,9 +223,16 @@ if (__DEV__) {
208223

209224
// Remove our event listeners
210225
window.removeEventListener('error', handleWindowError);
211-
};
212226

213-
invokeGuardedCallbackImpl = invokeGuardedCallbackDev;
227+
if (!didCall) {
228+
// Something went really wrong, and our event was not dispatched.
229+
// https://github.com/facebook/react/issues/16734
230+
// https://github.com/facebook/react/issues/16585
231+
// Fall back to the production implementation.
232+
restoreAfterDispatch();
233+
return invokeGuardedCallbackProd.apply(this, arguments);
234+
}
235+
};
214236
}
215237
}
216238

0 commit comments

Comments
 (0)