@samitouri / QOS-React-2 / commits / f918b0eb41

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

* Fix development mode hang when iframe is removed * Also fix #16734

Dan Abramov committed Jul 1, 2020 at 15:33 UTC f918b0eb41e0eabb8e4dc2bcd20fb8a25654d68f
1 file changed +33 -11
packages/shared/invokeGuardedCallbackImpl.js
+33 -11
@@ -9,7 +9,7 @@
9
10 import invariant from 'shared/invariant';
11
12 -let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
12 +function invokeGuardedCallbackProd<A, B, C, D, E, F, Context>(
13 name: string | null,
14 func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
15 context: Context,
@@ -26,7 +26,9 @@ let invokeGuardedCallbackImpl = function<A, B, C, D, E, F, Context>(
26 } catch (error) {
27 this.onError(error);
28 }
29 -};
29 +}
30 +
31 +let invokeGuardedCallbackImpl = invokeGuardedCallbackProd;
32
33 if (__DEV__) {
34 // In DEV mode, we swap out invokeGuardedCallback for a special version
@@ -58,7 +60,15 @@ if (__DEV__) {
60 ) {
61 const fakeNode = document.createElement('react');
62
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 + >(
72 name: string | null,
73 func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
74 context: Context,
@@ -85,6 +95,7 @@ if (__DEV__) {
95 );
96 const evt = document.createEvent('Event');
97
98 + let didCall = false;
99 // Keeps track of whether the user-provided callback threw an error. We
100 // set this to true at the beginning, then set it to false right after
101 // calling the function. If the function errors, `didError` will never be
@@ -105,11 +116,7 @@ if (__DEV__) {
116 'event',
117 );
118
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() {
120 // We immediately remove the callback from event listeners so that
121 // nested `invokeGuardedCallback` calls do not clash. Otherwise, a
122 // nested call would trigger the fake event handlers of any call higher
@@ -126,7 +133,15 @@ if (__DEV__) {
133 ) {
134 window.event = windowEvent;
135 }
136 + }
137
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();
145 func.apply(context, funcArgs);
146 didError = false;
147 }
@@ -183,7 +198,7 @@ if (__DEV__) {
198 Object.defineProperty(window, 'event', windowEventDescriptor);
199 }
200
186 - if (didError) {
201 + if (didCall && didError) {
202 if (!didSetError) {
203 // The callback errored, but the error event never fired.
204 error = new Error(
@@ -208,9 +223,16 @@ if (__DEV__) {
223
224 // Remove our event listeners
225 window.removeEventListener('error', handleWindowError);
211 - };
226
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 + };
236 }
237 }
238