From c86ae4b46fc9ff338dae785f529b6a78571abba7 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 25 Oct 2017 14:13:34 -0700 Subject: [PATCH] Added naive scheduleDeferredCallback implementation for RN (#11362) * Added naive scheduleDeferredCallback implementation for RN * Fixed ReactNative shim's expected @provideModule --- .../src/ReactNativeFiberRenderer.js | 12 ++-- .../src/ReactNativeFrameScheduling.js | 65 +++++++++++++++++++ .../rollup/shims/react-native/ReactNative.js | 4 +- 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 packages/react-native-renderer/src/ReactNativeFrameScheduling.js diff --git a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js index 61a9141abeef4..ce410874dbeaa 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js @@ -21,6 +21,7 @@ const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationI const ReactNativeAttributePayload = require('./ReactNativeAttributePayload'); const ReactNativeComponentTree = require('./ReactNativeComponentTree'); const ReactNativeFiberHostComponent = require('./ReactNativeFiberHostComponent'); +const ReactNativeFrameScheduling = require('./ReactNativeFrameScheduling'); const ReactNativeTagHandles = require('./ReactNativeTagHandles'); const ReactNativeViewConfigRegistry = require('./ReactNativeViewConfigRegistry'); const { @@ -157,6 +158,8 @@ const NativeRenderer = ReactFiberReconciler({ return instance; }, + now: ReactNativeFrameScheduling.now, + prepareForCommit(): void { // Noop }, @@ -176,12 +179,12 @@ const NativeRenderer = ReactFiberReconciler({ // Noop }, + scheduleDeferredCallback: ReactNativeFrameScheduling.scheduleDeferredCallback, + shouldDeprioritizeSubtree(type: string, props: Props): boolean { return false; }, - scheduleDeferredCallback: global.requestIdleCallback, - shouldSetTextContent(type: string, props: Props): boolean { // TODO (bvaughn) Revisit this decision. // Always returning false simplifies the createInstance() implementation, @@ -194,11 +197,6 @@ const NativeRenderer = ReactFiberReconciler({ useSyncScheduling: true, - now(): number { - // TODO: Enable expiration by implementing this method. - return 0; - }, - mutation: { appendChild( parentInstance: Instance, diff --git a/packages/react-native-renderer/src/ReactNativeFrameScheduling.js b/packages/react-native-renderer/src/ReactNativeFrameScheduling.js new file mode 100644 index 0000000000000..e1fbebdf0c7b1 --- /dev/null +++ b/packages/react-native-renderer/src/ReactNativeFrameScheduling.js @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +'use strict'; + +import type {Deadline} from 'react-reconciler'; + +const hasNativePerformanceNow = + typeof performance === 'object' && typeof performance.now === 'function'; + +const now = hasNativePerformanceNow + ? () => performance.now() + : () => Date.now(); + +type Callback = (deadline: Deadline) => void; + +let isCallbackScheduled: boolean = false; +let scheduledCallback: Callback | null = null; +let frameDeadline: number = 0; + +const frameDeadlineObject: Deadline = { + timeRemaining: () => frameDeadline - now(), +}; + +function setTimeoutCallback() { + isCallbackScheduled = false; + + // TODO (bvaughn) Hard-coded 5ms unblocks initial async testing. + // React API probably changing to boolean rather than time remaining. + // Longer-term plan is to rewrite this using shared memory, + // And just return the value of the bit as the boolean. + frameDeadline = now() + 5; + + const callback = scheduledCallback; + scheduledCallback = null; + if (callback !== null) { + callback(frameDeadlineObject); + } +} + +// RN has a poor polyfill for requestIdleCallback so we aren't using it. +// This implementation is only intended for short-term use anyway. +// We also don't implement cancel functionality b'c Fiber doesn't currently need it. +function scheduleDeferredCallback(callback: Callback): number { + // We assume only one callback is scheduled at a time b'c that's how Fiber works. + scheduledCallback = callback; + + if (!isCallbackScheduled) { + isCallbackScheduled = true; + setTimeout(setTimeoutCallback, 1); + } + + return 0; +} + +module.exports = { + now, + scheduleDeferredCallback, +}; diff --git a/scripts/rollup/shims/react-native/ReactNative.js b/scripts/rollup/shims/react-native/ReactNative.js index 35b7c3909512a..a648cafbc30c2 100644 --- a/scripts/rollup/shims/react-native/ReactNative.js +++ b/scripts/rollup/shims/react-native/ReactNative.js @@ -14,9 +14,9 @@ import type {ReactNativeType} from 'ReactNativeTypes'; let ReactNative; if (__DEV__) { - ReactNative = require('ReactNativeFiber-dev'); + ReactNative = require('ReactNativeRenderer-dev'); } else { - ReactNative = require('ReactNativeFiber-prod'); + ReactNative = require('ReactNativeRenderer-prod'); } module.exports = (ReactNative: ReactNativeType);