From a2cd55aee0b1ca7823f9fe9377edf795468beeff Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Thu, 21 Jun 2018 11:45:23 -0700 Subject: [PATCH 1/5] Fix crash during server render. setTimeout and clearTimeout may not be available in some server-render environments (such as ChakraCore in React.NET), and loading ReactScheduler.js will cause a crash unless the existence of the variables are checked via a typeof comparison. https://github.com/reactjs/React.NET/issues/555 The crash did not occur in 16.4.0, and the change appears to have been introduced here: https://github.com/facebook/react/pull/12931/files#diff-bbebc3357e1fb99ab13ad796e04b69a6L47 I tested this by using yarn link and running it with a local copy of React.NET. I am unsure the best way to unit test this change, since assigning null to `setTimeout` causes an immediate crash within the Node REPL. --- packages/react-scheduler/src/ReactScheduler.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index 2b016f1275739..4ee113f74b932 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -60,8 +60,9 @@ if (__DEV__) { // this module is initially evaluated. // We want to be using a consistent implementation. const localDate = Date; -const localSetTimeout = setTimeout; -const localClearTimeout = clearTimeout; +const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null; +const localClearTimeout = + typeof clearTimeout === 'function' ? clearTimeout : null; const hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; From d29667acc5c9cf45aef228cbdd9ecf2d01ebc784 Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Thu, 21 Jun 2018 12:44:32 -0700 Subject: [PATCH 2/5] Fix flow errors and log warning if setTimeout / clearTimeout are not defined / not a function. --- packages/react-scheduler/src/ReactScheduler.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index 4ee113f74b932..4f38509bcb660 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -60,9 +60,21 @@ if (__DEV__) { // this module is initially evaluated. // We want to be using a consistent implementation. const localDate = Date; -const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null; +const localSetTimeout = + typeof setTimeout === 'function' + ? setTimeout + : warning( + false, + 'setTimeout is not a function. Please load a polyfill that defines this function.', + ); + const localClearTimeout = - typeof clearTimeout === 'function' ? clearTimeout : null; + typeof clearTimeout === 'function' + ? clearTimeout + : warning( + false, + 'clearTimeout is not a function. Please load a polyfill that defines this function.', + ); const hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; From 98afa723a5995aee4609515df8648c344255ef53 Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Thu, 21 Jun 2018 13:38:38 -0700 Subject: [PATCH 3/5] Use invariant to assert setTimeout / clearTimeout are functions --- .../react-scheduler/src/ReactScheduler.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index 4f38509bcb660..da0d283407803 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -43,6 +43,7 @@ export type CallbackIdType = CallbackConfigType; import {canUseDOM} from 'shared/ExecutionEnvironment'; import warning from 'shared/warning'; +import invariant from 'shared/invariant'; if (__DEV__) { if (canUseDOM && typeof requestAnimationFrame !== 'function') { @@ -61,20 +62,9 @@ if (__DEV__) { // We want to be using a consistent implementation. const localDate = Date; const localSetTimeout = - typeof setTimeout === 'function' - ? setTimeout - : warning( - false, - 'setTimeout is not a function. Please load a polyfill that defines this function.', - ); - + typeof setTimeout === 'function' ? setTimeout : undefined; const localClearTimeout = - typeof clearTimeout === 'function' - ? clearTimeout - : warning( - false, - 'clearTimeout is not a function. Please load a polyfill that defines this function.', - ); + typeof clearTimeout === 'function' ? clearTimeout : undefined; const hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; @@ -104,6 +94,11 @@ if (!canUseDOM) { callback: FrameCallbackType, options?: {timeout: number}, ): CallbackIdType { + invariant( + typeof localSetTimeout === 'function', + 'localSetTimeout is not a function. Please load a polyfill that defines this function.', + ); + // keeping return type consistent const callbackConfig = { scheduledCallback: callback, @@ -123,6 +118,11 @@ if (!canUseDOM) { return callbackConfig; }; cancelScheduledWork = function(callbackId: CallbackIdType) { + invariant( + typeof localClearTimeout === 'function', + 'localClearTimeout is not a function. Please load a polyfill that defines this function.', + ); + const callback = callbackId.scheduledCallback; const timeoutId = timeoutIds.get(callback); timeoutIds.delete(callbackId); From ffd89fdb6dff7529f59702c9c405f4b8390f7f48 Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Fri, 22 Jun 2018 11:06:34 -0700 Subject: [PATCH 4/5] Remove use of invariant --- packages/react-scheduler/src/ReactScheduler.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index da0d283407803..76035cca92a4e 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -43,7 +43,6 @@ export type CallbackIdType = CallbackConfigType; import {canUseDOM} from 'shared/ExecutionEnvironment'; import warning from 'shared/warning'; -import invariant from 'shared/invariant'; if (__DEV__) { if (canUseDOM && typeof requestAnimationFrame !== 'function') { @@ -62,9 +61,9 @@ if (__DEV__) { // We want to be using a consistent implementation. const localDate = Date; const localSetTimeout = - typeof setTimeout === 'function' ? setTimeout : undefined; + typeof setTimeout === 'function' ? setTimeout : (undefined: any); const localClearTimeout = - typeof clearTimeout === 'function' ? clearTimeout : undefined; + typeof clearTimeout === 'function' ? clearTimeout : (undefined: any); const hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; @@ -94,11 +93,6 @@ if (!canUseDOM) { callback: FrameCallbackType, options?: {timeout: number}, ): CallbackIdType { - invariant( - typeof localSetTimeout === 'function', - 'localSetTimeout is not a function. Please load a polyfill that defines this function.', - ); - // keeping return type consistent const callbackConfig = { scheduledCallback: callback, @@ -118,11 +112,6 @@ if (!canUseDOM) { return callbackConfig; }; cancelScheduledWork = function(callbackId: CallbackIdType) { - invariant( - typeof localClearTimeout === 'function', - 'localClearTimeout is not a function. Please load a polyfill that defines this function.', - ); - const callback = callbackId.scheduledCallback; const timeoutId = timeoutIds.get(callback); timeoutIds.delete(callbackId); From 96881ff600f59f3945b757eeaa2afe7993f08997 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 22 Jun 2018 20:07:38 +0100 Subject: [PATCH 5/5] Explain --- packages/react-scheduler/src/ReactScheduler.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-scheduler/src/ReactScheduler.js b/packages/react-scheduler/src/ReactScheduler.js index 76035cca92a4e..15a0ec54bdd4e 100644 --- a/packages/react-scheduler/src/ReactScheduler.js +++ b/packages/react-scheduler/src/ReactScheduler.js @@ -60,6 +60,11 @@ if (__DEV__) { // this module is initially evaluated. // We want to be using a consistent implementation. const localDate = Date; + +// This initialization code may run even on server environments +// if a component just imports ReactDOM (e.g. for findDOMNode). +// Some environments might not have setTimeout or clearTimeout. +// https://github.com/facebook/react/pull/13088 const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : (undefined: any); const localClearTimeout =