Files
react/packages/react-rt-renderer/src/ReactNativeRTFiberRenderer.js
T
Andrew ClarkandGitHub 9b36df86c6 Use requestIdleCallback timeout to force expiration (#11548)
* Don't call idle callback unless there's time remaining

* Expiration fixture

Fixture that demonstrates how async work expires after a certain interval.
The fixture clogs the main thread with animation work, so it only works if the
`timeout` option is provided to `requestIdleCallback`.

* Pass timeout option to requestIdleCallback

Forces `requestIdleCallback` to fire if too much time has elapsed, even if the
main thread is busy. Required to make expiration times work properly. Otherwise,
async work can expire, but React never has a chance to flush it because the
browser never calls into React.
2017-11-15 13:46:17 -08:00

246 lines
5.5 KiB
JavaScript

/**
* 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
*/
import ReactFiberReconciler from 'react-reconciler';
import emptyObject from 'fbjs/lib/emptyObject';
import invariant from 'fbjs/lib/invariant';
// Module provided by RN:
import RTManager from 'RTManager';
import {
precacheFiberNode,
updateFiberProps,
} from './ReactNativeRTComponentTree';
import ReactNativeRTTagHandles from './ReactNativeRTTagHandles';
export type Container = number;
export type Instance = number;
export type Props = Object;
export type TextInstance = number;
function processProps(instance: number, props: Props): Object {
const propsPayload = {};
for (var key in props) {
if (key === 'children') {
// Skip special case.
continue;
}
var value = props[key];
if (typeof value === 'function') {
value = {
style: 'rt-event',
event: key,
tag: instance,
};
}
propsPayload[key] = value;
}
return propsPayload;
}
function arePropsEqual(oldProps: Props, newProps: Props): boolean {
var key;
for (key in newProps) {
if (key === 'children') {
// Skip special case.
continue;
}
if (newProps[key] !== oldProps[key]) {
return false;
}
}
for (key in oldProps) {
if (key === 'children') {
// Skip special case.
continue;
}
if (!(key in newProps)) {
return false;
}
}
return true;
}
const NativeRTRenderer = ReactFiberReconciler({
appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
RTManager.appendChild(parentInstance, child);
},
createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: {},
internalInstanceHandle: Object,
): Instance {
const tag = ReactNativeRTTagHandles.allocateTag();
precacheFiberNode(internalInstanceHandle, tag);
updateFiberProps(tag, props);
RTManager.createNode(tag, type, processProps(tag, props));
return tag;
},
createTextInstance(
text: string,
rootContainerInstance: Container,
hostContext: {},
internalInstanceHandle: Object,
): TextInstance {
invariant(false, 'Text components are not supported for now.');
},
finalizeInitialChildren(
parentInstance: Instance,
type: string,
props: Props,
rootContainerInstance: Container,
): boolean {
return false;
},
getRootHostContext(): {} {
return emptyObject;
},
getChildHostContext(): {} {
return emptyObject;
},
getPublicInstance(instance) {
return instance;
},
prepareForCommit(): void {
RTManager.beginUpdates();
},
prepareUpdate(
instance: Instance,
type: string,
oldProps: Props,
newProps: Props,
rootContainerInstance: Container,
hostContext: {},
): null | Object {
if (arePropsEqual(oldProps, newProps)) {
return null;
}
return processProps(instance, newProps);
},
resetAfterCommit(): void {
RTManager.completeUpdates();
},
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
scheduleDeferredCallback: global.requestIdleCallback,
cancelDeferredCallback: global.cancelIdleCallback,
shouldSetTextContent(type: string, props: Props): boolean {
// TODO: Figure out when we should allow text content.
return false;
},
useSyncScheduling: true,
now(): number {
// TODO: Enable expiration by implementing this method.
return 0;
},
mutation: {
appendChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
RTManager.appendChild(parentInstance, child);
},
appendChildToContainer(
parentInstance: Container,
child: Instance | TextInstance,
): void {
RTManager.appendChildToContext(parentInstance, child);
},
commitTextUpdate(
textInstance: TextInstance,
oldText: string,
newText: string,
): void {
invariant(false, 'Text components are not yet supported.');
},
commitMount(
instance: Instance,
type: string,
newProps: Props,
internalInstanceHandle: Object,
): void {
// Noop
},
commitUpdate(
instance: Instance,
updatePayload: Object,
type: string,
oldProps: Props,
newProps: Props,
internalInstanceHandle: Object,
): void {
updateFiberProps(instance, newProps);
RTManager.updateNode(instance, updatePayload);
},
insertBefore(
parentInstance: Instance,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance,
): void {
RTManager.prependChild(child, beforeChild);
},
insertInContainerBefore(
parentInstance: Container,
child: Instance | TextInstance,
beforeChild: Instance | TextInstance,
): void {
RTManager.prependChild(child, beforeChild);
},
removeChild(
parentInstance: Instance,
child: Instance | TextInstance,
): void {
// TODO: recursively uncache, by traversing fibers, this will currently leak
RTManager.deleteChild(child);
},
removeChildFromContainer(
parentInstance: Container,
child: Instance | TextInstance,
): void {
// TODO: recursively uncache, by traversing fibers, this will currently leak
RTManager.deleteChild(child);
},
resetTextContent(instance: Instance): void {
// Noop
},
},
});
export default NativeRTRenderer;