From 98f747f65dfd5ba1ee5ff6d1c96ab7c322af18ab Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 17:50:58 +0100 Subject: [PATCH 01/19] Make renderer work with react-native master --- flow/react-native-host-hooks.js | 2 +- src/renderers/native/ReactNativeInjection.js | 2 +- src/renderers/native/ReactNativeStackInjection.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js index 7c0a1b5c3910..b52951fcd3a3 100644 --- a/flow/react-native-host-hooks.js +++ b/flow/react-native-host-hooks.js @@ -18,7 +18,7 @@ declare module 'deepFreezeAndThrowOnMutationInDev' { declare function exports(obj : T) : T; } declare module 'flattenStyle' { } -declare module 'InitializeJavaScriptAppEngine' { } +declare module 'InitializeCore' { } declare module 'RCTEventEmitter' { declare function register() : void; } diff --git a/src/renderers/native/ReactNativeInjection.js b/src/renderers/native/ReactNativeInjection.js index 16b8564b588e..d3000c8d0a43 100644 --- a/src/renderers/native/ReactNativeInjection.js +++ b/src/renderers/native/ReactNativeInjection.js @@ -17,7 +17,7 @@ * ensures it exists in the dependency graph and can be `require`d. * TODO: require this in packager, not in React #10932517 */ -require('InitializeJavaScriptAppEngine'); +require('InitializeCore'); var EventPluginHub = require('EventPluginHub'); var EventPluginUtils = require('EventPluginUtils'); diff --git a/src/renderers/native/ReactNativeStackInjection.js b/src/renderers/native/ReactNativeStackInjection.js index 83a5bcd4d4b6..8b85a67e9bd5 100644 --- a/src/renderers/native/ReactNativeStackInjection.js +++ b/src/renderers/native/ReactNativeStackInjection.js @@ -17,7 +17,7 @@ * ensures it exists in the dependency graph and can be `require`d. * TODO: require this in packager, not in React #10932517 */ -require('InitializeJavaScriptAppEngine'); +require('InitializeCore'); var React = require('React'); var ReactComponentEnvironment = require('ReactComponentEnvironment'); From 1b72ea060b348e0c4aef2815f011c7738929207f Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 18:13:44 +0100 Subject: [PATCH 02/19] Add fiber feature flag for native --- .../native/ReactNativeFeatureFlags.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/renderers/native/ReactNativeFeatureFlags.js diff --git a/src/renderers/native/ReactNativeFeatureFlags.js b/src/renderers/native/ReactNativeFeatureFlags.js new file mode 100644 index 000000000000..8f9e36713e60 --- /dev/null +++ b/src/renderers/native/ReactNativeFeatureFlags.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactNativeFeatureFlags + * @flow + */ + +'use strict'; + + +var ReactNativeFeatureFlags = { + useFiber: true, +}; + +module.exports = ReactNativeFeatureFlags; From 3b0ea969b846fb896dc195d7d694a0904e554b6e Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 18:26:27 +0100 Subject: [PATCH 03/19] Restructue native renderer entry Renamed ReactNative.js to ReactNativeStack.js ReactNative.js is the new entry which checks if it is gonna use stack or fiber. Check is done using ReactNativeFeatureFlags.useFiber --- src/renderers/native/ReactNative.js | 67 ++-------------- src/renderers/native/ReactNativeStack.js | 77 +++++++++++++++++++ .../native/fiber/ReactNativeFiber.js | 13 ++++ 3 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 src/renderers/native/ReactNativeStack.js create mode 100644 src/renderers/native/fiber/ReactNativeFiber.js diff --git a/src/renderers/native/ReactNative.js b/src/renderers/native/ReactNative.js index 6112e9045b2b..9689caeb4758 100644 --- a/src/renderers/native/ReactNative.js +++ b/src/renderers/native/ReactNative.js @@ -9,69 +9,16 @@ * @providesModule ReactNative * @flow */ -'use strict'; - -// Require ReactNativeDefaultInjection first for its side effects of setting up -// the JS environment -var ReactNativeComponentTree = require('ReactNativeComponentTree'); -var ReactNativeInjection = require('ReactNativeInjection'); -var ReactNativeStackInjection = require('ReactNativeStackInjection'); - -var ReactNativeMount = require('ReactNativeMount'); -var ReactUpdates = require('ReactUpdates'); - -var findNodeHandle = require('findNodeHandle'); -ReactNativeInjection.inject(); -ReactNativeStackInjection.inject(); - -var render = function( - element: ReactElement, - mountInto: number, - callback?: ?(() => void) -): ?ReactComponent { - return ReactNativeMount.renderComponent(element, mountInto, callback); -}; - -var ReactNative = { - hasReactNativeInitialized: false, - findNodeHandle: findNodeHandle, - render: render, - unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode, - - /* eslint-disable camelcase */ - unstable_batchedUpdates: ReactUpdates.batchedUpdates, - /* eslint-enable camelcase */ +'use strict'; - unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer, -}; +var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); -// Inject the runtime into a devtools global hook regardless of browser. -// Allows for debugging when the hook is injected on the page. -/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */ -if ( - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') { - __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ - ComponentTree: { - getClosestInstanceFromNode: function(node) { - return ReactNativeComponentTree.getClosestInstanceFromNode(node); - }, - getNodeFromInstance: function(inst) { - // inst is an internal instance (but could be a composite) - while (inst._renderedComponent) { - inst = inst._renderedComponent; - } - if (inst) { - return ReactNativeComponentTree.getNodeFromInstance(inst); - } else { - return null; - } - }, - }, - Mount: ReactNativeMount, - Reconciler: require('ReactReconciler'), - }); +var ReactNative; +if (ReactNativeFeatureFlags.useFiber) { + ReactNative = require('ReactNativeFiber'); +} else { + ReactNative = require('ReactNativeStack'); } module.exports = ReactNative; diff --git a/src/renderers/native/ReactNativeStack.js b/src/renderers/native/ReactNativeStack.js new file mode 100644 index 000000000000..7d4e76a6d989 --- /dev/null +++ b/src/renderers/native/ReactNativeStack.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactNativeStack + * @flow + */ +'use strict'; + +// Require ReactNativeDefaultInjection first for its side effects of setting up +// the JS environment +var ReactNativeComponentTree = require('ReactNativeComponentTree'); +var ReactNativeInjection = require('ReactNativeInjection'); +var ReactNativeStackInjection = require('ReactNativeStackInjection'); + +var ReactNativeMount = require('ReactNativeMount'); +var ReactUpdates = require('ReactUpdates'); + +var findNodeHandle = require('findNodeHandle'); + +ReactNativeInjection.inject(); +ReactNativeStackInjection.inject(); + +var render = function( + element: ReactElement, + mountInto: number, + callback?: ?(() => void) +): ?ReactComponent { + return ReactNativeMount.renderComponent(element, mountInto, callback); +}; + +var ReactNative = { + hasReactNativeInitialized: false, + findNodeHandle: findNodeHandle, + render: render, + unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode, + + /* eslint-disable camelcase */ + unstable_batchedUpdates: ReactUpdates.batchedUpdates, + /* eslint-enable camelcase */ + + unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer, +}; + +// Inject the runtime into a devtools global hook regardless of browser. +// Allows for debugging when the hook is injected on the page. +/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */ +if ( + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') { + __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ + ComponentTree: { + getClosestInstanceFromNode: function(node) { + return ReactNativeComponentTree.getClosestInstanceFromNode(node); + }, + getNodeFromInstance: function(inst) { + // inst is an internal instance (but could be a composite) + while (inst._renderedComponent) { + inst = inst._renderedComponent; + } + if (inst) { + return ReactNativeComponentTree.getNodeFromInstance(inst); + } else { + return null; + } + }, + }, + Mount: ReactNativeMount, + Reconciler: require('ReactReconciler'), + }); +} + +module.exports = ReactNative; diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js new file mode 100644 index 000000000000..41b3ce8ff616 --- /dev/null +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -0,0 +1,13 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactNativeFiber + * @flow + */ + +'use strict'; From 526dfa7eb45926d314cb2f04cce2dc18a8b56cd3 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 20:16:54 +0100 Subject: [PATCH 04/19] make findNodeHandle work for Fiber and Stack --- .../native/ReactNativeStackInjection.js | 7 ++++++ .../native/fiber/ReactNativeFiber.js | 6 +++++ src/renderers/native/findNodeHandle.js | 24 +++++++++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/renderers/native/ReactNativeStackInjection.js b/src/renderers/native/ReactNativeStackInjection.js index 8b85a67e9bd5..eb30c8f5de64 100644 --- a/src/renderers/native/ReactNativeStackInjection.js +++ b/src/renderers/native/ReactNativeStackInjection.js @@ -29,6 +29,8 @@ var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment') var ReactNativeTextComponent = require('ReactNativeTextComponent'); var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent'); var ReactUpdates = require('ReactUpdates'); +var getHostComponentFromComposite = require('getHostComponentFromComposite'); +var findNodeHandle = require('findNodeHandle'); var invariant = require('invariant'); @@ -74,6 +76,11 @@ function inject() { } invariant(false, 'Expected a component class, got %s.%s', tag, info); }); + + findNodeHandle._injectStack(function(inst) { + return inst.getHostNode(); + }); + } module.exports = { diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 41b3ce8ff616..2c311b74d26f 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -11,3 +11,9 @@ */ 'use strict'; + +var findNodeHandle = require('findNodeHandle'); + +findNodeHandle._injectFiber(function(fiber: Fiber) { + return NativeRenderer.findHostInstance(fiber)._rootNodeID; +}); diff --git a/src/renderers/native/findNodeHandle.js b/src/renderers/native/findNodeHandle.js index aca064b52954..5eb3c53a2b04 100644 --- a/src/renderers/native/findNodeHandle.js +++ b/src/renderers/native/findNodeHandle.js @@ -20,6 +20,13 @@ var warning = require('warning'); import type { ReactInstance } from 'ReactInstanceType'; +let findFiber = function(arg) { + invariant(false, 'Missing injection for fiber findDOMNode'); +}; +let findStack = function(arg) { + invariant(false, 'Missing injection for stack findDOMNode'); +}; + /** * ReactNative vs ReactWeb * ----------------------- @@ -80,8 +87,13 @@ function findNodeHandle(componentOrHandle: any): ?number { // TODO (balpert): Wrap iOS native components in a composite wrapper, then // ReactInstanceMap.get here will always succeed for mounted components - var internalInstance = ReactInstanceMap.get(component); - if (internalInstance) { + var inst = ReactInstanceMap.get(component); + if (inst) { + if (typeof inst.tag === 'number') { + return findFiber(inst); + } else { + return findStack(inst); + } return internalInstance.getHostNode(); } else { var rootNodeID = component._rootNodeID; @@ -112,4 +124,12 @@ function findNodeHandle(componentOrHandle: any): ?number { } } +findNodeHandle._injectFiber = function(fn) { + findFiber = fn; +}; +findNodeHandle._injectStack = function(fn) { + findStack = fn; +}; + + module.exports = findNodeHandle; From 30aec7fe737673bf250b362ae7834a53d8e0e524 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 22:07:00 +0100 Subject: [PATCH 05/19] make global responder handler compatible with Fiber --- src/renderers/native/ReactNativeGlobalResponderHandler.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderers/native/ReactNativeGlobalResponderHandler.js b/src/renderers/native/ReactNativeGlobalResponderHandler.js index 84bd45fb6f1f..c0534f4202bf 100644 --- a/src/renderers/native/ReactNativeGlobalResponderHandler.js +++ b/src/renderers/native/ReactNativeGlobalResponderHandler.js @@ -15,6 +15,9 @@ var UIManager = require('UIManager'); var ReactNativeGlobalResponderHandler = { onChange: function(from, to, blockNativeResponder) { if (to !== null) { + if (typeof to.tag === 'number') { + to = to.stateNode; + } UIManager.setJSResponder( to._rootNodeID, blockNativeResponder From 5c68209bb039bd2a7701acfe09e77718c6c82511 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 22:07:35 +0100 Subject: [PATCH 06/19] make ReactNativeComponentTree compatible with Fiber --- src/renderers/native/ReactNativeComponentTree.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/renderers/native/ReactNativeComponentTree.js b/src/renderers/native/ReactNativeComponentTree.js index 7b16b8512a17..27038e71d227 100644 --- a/src/renderers/native/ReactNativeComponentTree.js +++ b/src/renderers/native/ReactNativeComponentTree.js @@ -51,8 +51,17 @@ function getInstanceFromTag(tag) { } function getTagFromInstance(inst) { + if (typeof inst.tag === 'number') { + inst = inst.stateNode; + } + invariant(inst._rootNodeID, 'All native instances should have a tag.'); return inst._rootNodeID; + +} + +function precacheFiberNode(inst, tag) { + instanceCache[tag] = inst; } var ReactNativeComponentTree = { @@ -61,6 +70,7 @@ var ReactNativeComponentTree = { getNodeFromInstance: getTagFromInstance, precacheNode: precacheNode, uncacheNode: uncacheNode, + precacheFiberNode: precacheFiberNode, }; module.exports = ReactNativeComponentTree; From 866b15f0717c1a80d319cbf6635222611547ad18 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 22:08:33 +0100 Subject: [PATCH 07/19] create React class component if we use Fiber --- .../native/createReactNativeComponentClass.js | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/renderers/native/createReactNativeComponentClass.js b/src/renderers/native/createReactNativeComponentClass.js index e5f113bf0bbe..7079f7a4986c 100644 --- a/src/renderers/native/createReactNativeComponentClass.js +++ b/src/renderers/native/createReactNativeComponentClass.js @@ -12,7 +12,10 @@ 'use strict'; +var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); var ReactNativeBaseComponent = require('ReactNativeBaseComponent'); +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); // See also ReactNativeBaseComponent type ReactNativeBaseComponentViewConfig = { @@ -28,19 +31,35 @@ type ReactNativeBaseComponentViewConfig = { var createReactNativeComponentClass = function( viewConfig: ReactNativeBaseComponentViewConfig ): ReactClass { - var Constructor = function(element) { - this._currentElement = element; - this._topLevelWrapper = null; - this._hostParent = null; - this._hostContainerInfo = null; - this._rootNodeID = 0; - this._renderedChildren = null; - }; - Constructor.displayName = viewConfig.uiViewClassName; - Constructor.viewConfig = viewConfig; - Constructor.propTypes = viewConfig.propTypes; - Constructor.prototype = new ReactNativeBaseComponent(viewConfig); - Constructor.prototype.constructor = Constructor; + var Constructor; + if (ReactNativeFeatureFlags.useFiber) { + Constructor = React.createClass({ + displayName: viewConfig.uiViewClassName, + propTypes: viewConfig.propTypes, + viewConfig: viewConfig, + statics: { + viewConfig: viewConfig, + }, + mixins: [NativeMethodsMixin], + render() { + return React.createElement(viewConfig.uiViewClassName, this.props); + } + }); + } else { + Constructor = function(element) { + this._currentElement = element; + this._topLevelWrapper = null; + this._hostParent = null; + this._hostContainerInfo = null; + this._rootNodeID = 0; + this._renderedChildren = null; + }; + Constructor.prototype = new ReactNativeBaseComponent(viewConfig); + Constructor.prototype.constructor = Constructor; + Constructor.displayName = viewConfig.uiViewClassName; + Constructor.viewConfig = viewConfig; + Constructor.propTypes = viewConfig.propTypes; + } return ((Constructor: any): ReactClass); }; From c5926b49120158b7453f6f56bfba1e4bc667fd3b Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 22:27:47 +0100 Subject: [PATCH 08/19] ReactNativeFiber --- .../native/fiber/ReactNativeFiber.js | 252 +++++++++++++++++- 1 file changed, 251 insertions(+), 1 deletion(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 2c311b74d26f..36d3914cefbb 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -12,8 +12,258 @@ 'use strict'; +import type { Fiber } from 'ReactFiber'; +import type { HostChildren } from 'ReactFiberReconciler'; +var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); +var ReactFiberReconciler = require('ReactFiberReconciler'); +var ReactNativeComponentTree = require('ReactNativeComponentTree'); +var ReactNativeTagHandles = require('ReactNativeTagHandles'); +var ReactNativeInjection = require('ReactNativeInjection'); +var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); +var UIManager = require('UIManager'); var findNodeHandle = require('findNodeHandle'); +var warning = require('warning'); + +var { precacheFiberNode } = ReactNativeComponentTree; + +ReactNativeInjection.inject(); findNodeHandle._injectFiber(function(fiber: Fiber) { - return NativeRenderer.findHostInstance(fiber)._rootNodeID; + const inst = NativeRenderer.findHostInstance(fiber); + return inst ? inst._rootNodeID : null; +}); + +type Container = { + _tag: number; +}; +type Props = { [key: any]: any }; + +type Instance = { + parentTag?: number; + _rootNodeID: number; + children: Array; +}; +type TextInstance = { + parentTag?: number; + _rootNodeID: number; +}; + +function recursivelyAppendChildren(parent : Instance, child : HostChildren) { + if (!child) { + return; + } + if (typeof child._rootNodeID === 'number') { + parent.children.push(child._rootNodeID); + } else { + /* As a result of the refinement issue this type isn't known. */ + let node : any = child; + do { + recursivelyAppendChildren(parent, node.output); + } while (node = node.sibling); + } +} + +var NativeRenderer = ReactFiberReconciler({ + + updateContainer(container : number, children : HostChildren) : void { + if (children && children.parentTag !== container) { + const tempNode = { _rootNodeID: 0, children: [] }; + recursivelyAppendChildren(tempNode, children); + // clear current children UIManager.removeSubview + UIManager.setChildren(container, tempNode.children); + children.parentTag = container; + } + }, + + createInstance( + type : string, + props : Props, + children : HostChildren, + internalInstanceHandle : Object + ) : Instance { + const { viewConfig } = internalInstanceHandle.return.type; + const root = 1; // HACK + const node = { + _rootNodeID: ReactNativeTagHandles.allocateTag(), + children: [], + }; + precacheFiberNode(internalInstanceHandle, node._rootNodeID); + + if (typeof props.children === 'string' || typeof props.children === 'number') { + // create text node + const textTag = ReactNativeTagHandles.allocateTag(); + const text = '' + props.children; + UIManager.createView(textTag, 'RCTRawText', root, { text: text }); + node.children.push(textTag); + } + recursivelyAppendChildren(node, children); + const attributes = ReactNativeAttributePayload.create( + props, + viewConfig.validAttributes + ); + + UIManager.createView(node._rootNodeID, type, root, attributes); + UIManager.setChildren(node._rootNodeID, node.children.slice()); + return node; + }, + + prepareUpdate( + node : Instance, + oldProps : Props, + newProps : Props + ) : boolean { + return true; + }, + + commitUpdate(node : Instance, oldProps : Props, newProps : Props, internalInstanceHandle: Object) : void { + var root = 1; // HACK + const { viewConfig } = internalInstanceHandle.return.type; + const oldChildren = oldProps.children; + const newChildren = newProps.children; + if (typeof oldChildren === 'string' || oldChildren === 'number') { + const textTag = node.children[0]; + if (typeof newChildren === 'string' || typeof newChildren === 'number') { + UIManager.updateView(textTag, 'RCTRawText', { text: '' + newChildren }); + } else { + // TODO: does this work? + // will appendChild/insertBefore be called before this? if so this won't work + UIManager.manageChildren(node._rootNodeID, [], [], [], [], [0]); + node.children.splice(0, 1); + } + } + + const updatePayload = ReactNativeAttributePayload.diff( + oldProps, + newProps, + viewConfig.validAttributes + ); + if (updatePayload) { + UIManager.updateView( + node._rootNodeID, + viewConfig.uiViewClassName, + updatePayload + ); + } + }, + + createTextInstance(text: string, internalInstanceHandle : Object) : TextInstance { + const root = 1; // HACK + const node : TextInstance = { + _rootNodeID: ReactNativeTagHandles.allocateTag(), + }; + precacheFiberNode(internalInstanceHandle, node._rootNodeID); + UIManager.createView(node._rootNodeID, 'RCTRawText', root, { text: text }); + return node; + }, + + commitTextUpdate(textInstance : TextInstance, oldText : string, newText : string) : void { + if (oldText !== newText) { + UIManager.updateView( + textInstance._rootNodeID, + 'RCTRawText', + { text: newText } + ); + } + }, + + appendChild(parentInstance : Instance, child : Instance | TextInstance) : void { + parentInstance.children.push(child._rootNodeID); + UIManager.manageChildren( + parentInstance._rootNodeID, + [], + [], + [child._rootNodeID], + [parentInstance.children.length - 1 ], + [] + ); + }, + + insertBefore( + parentInstance : Instance, + child : Instance | TextInstance, + beforeChild : Instance | TextInstance + ) : void { + console.log('insert before'); + }, + + removeChild(parentInstance : Instance, child : Instance | TextInstance) : void { + const childIndex = parentInstance.children.indexOf(child._rootNodeID); + parentInstance.children.splice(childIndex, 1); + UIManager.manageChildren( + parentInstance._rootNodeID, + [], + [], + [], + [], + [childIndex] + ); + }, + + scheduleAnimationCallback: global.requestAnimationFrame, + + scheduleDeferredCallback: global.requestIdleCallback, + + useSyncScheduling: true, + }); + +var warned = false; + +function warnAboutUnstableUse() { + // Ignore this warning is the feature flag is turned on. E.g. for tests. + warning( + warned || ReactNativeFeatureFlags.useFiber, + 'You are using React DOM Fiber which is an experimental renderer. ' + + 'It is likely to have bugs, breaking changes and is unsupported.' + ); + warned = true; +} + +var _instancesByContainerID = {}; + +var ReactNative = { + + render(element : ReactElement, containerTag : number, callback: ?Function) { + warnAboutUnstableUse(); + if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) { + console.error('You cannot render into anything but a top root'); + return null; + } + + ReactNativeTagHandles.assertRootTag(containerTag); + + let root; + if (!_instancesByContainerID[containerTag]) { + root = NativeRenderer.mountContainer(element, containerTag, callback); + } else { + NativeRenderer.updateContainer( + element, + root = _instancesByContainerID[containerTag], + callback + ); + } + // console.log(root); + // is this correct? + _instancesByContainerID[containerTag] = root; + + return NativeRenderer.getPublicRootInstance(root); + }, + + unmountComponentAtNode(containerTag : number) { + warnAboutUnstableUse(); + const root = _instancesByContainerID[containerTag]; + _instancesByContainerID[containerTag] = null; + if (root) { + NativeRenderer.unmountContainer(root); + } + }, + + findNodeHandle: findNodeHandle, + + unstable_batchedUpdates(fn : () => A) : A { + return NativeRenderer.batchedUpdates(fn); + }, + +}; + +module.exports = ReactNative; From 782b883bf737e368765888a908a9f037362c9cc6 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Tue, 22 Nov 2016 22:29:16 +0100 Subject: [PATCH 09/19] temporary build tool --- .babelrc | 28 ++++----------------- gulpfile.js | 71 ++++++++++++++--------------------------------------- 2 files changed, 24 insertions(+), 75 deletions(-) diff --git a/.babelrc b/.babelrc index 3ba0e83fa1a9..7bc2f7913b05 100644 --- a/.babelrc +++ b/.babelrc @@ -1,28 +1,10 @@ { - "presets": ["react"], "ignore": ["third_party"], "plugins": [ - "transform-class-properties", + "syntax-flow", + "syntax-jsx", + "syntax-class-properties", "syntax-trailing-function-commas", - "transform-object-rest-spread", - "transform-es2015-template-literals", - "transform-es2015-literals", - "transform-es2015-arrow-functions", - "transform-es2015-block-scoped-functions", - ["transform-es2015-classes", { "loose": true }], - "transform-es2015-object-super", - "transform-es2015-shorthand-properties", - "transform-es2015-computed-properties", - "transform-es2015-for-of", - "check-es2015-constants", - ["transform-es2015-spread", { "loose": true }], - "transform-es2015-parameters", - ["transform-es2015-destructuring", { "loose": true }], - "transform-es2015-block-scoping", - "transform-es2015-modules-commonjs", - "transform-es3-member-expression-literals", - "transform-es3-property-literals", - "./scripts/babel/transform-object-assign-require", - "transform-react-jsx-source" - ] + "syntax-object-rest-spread" + ] } diff --git a/gulpfile.js b/gulpfile.js index dd086ba53404..f26b59f9455b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -76,7 +76,7 @@ var paths = { '!src/**/__tests__/**/*.js', '!src/**/__mocks__/**/*.js', ], - lib: 'build/node_modules/react-native/lib', + lib: '../react-native/Libraries/Renderer', }, reactTestRenderer: { src: [ @@ -139,19 +139,19 @@ var moduleMapReactDOM = Object.assign( moduleMapBase ); -var moduleMapReactNative = Object.assign( - { - // React Native Hooks - deepDiffer: 'react-native/lib/deepDiffer', - deepFreezeAndThrowOnMutationInDev: 'react-native/lib/deepFreezeAndThrowOnMutationInDev', - flattenStyle: 'react-native/lib/flattenStyle', - InitializeJavaScriptAppEngine: 'react-native/lib/InitializeJavaScriptAppEngine', - RCTEventEmitter: 'react-native/lib/RCTEventEmitter', - TextInputState: 'react-native/lib/TextInputState', - UIManager: 'react-native/lib/UIManager', - UIManagerStatTracker: 'react-native/lib/UIManagerStatTracker', - View: 'react-native/lib/View', - }, +var moduleMapReactNative = Object.assign({}, + //{ + // // React Native Hooks + // deepDiffer: 'react-native/lib/deepDiffer', + // deepFreezeAndThrowOnMutationInDev: 'react-native/lib/deepFreezeAndThrowOnMutationInDev', + // flattenStyle: 'react-native/lib/flattenStyle', + // InitializeJavaScriptAppEngine: 'react-native/lib/InitializeJavaScriptAppEngine', + // RCTEventEmitter: 'react-native/lib/RCTEventEmitter', + // TextInputState: 'react-native/lib/TextInputState', + // UIManager: 'react-native/lib/UIManager', + // UIManagerStatTracker: 'react-native/lib/UIManagerStatTracker', + // View: 'react-native/lib/View', + // }, rendererSharedState, moduleMapBase ); @@ -189,8 +189,7 @@ var babelOptsReactDOM = { var babelOptsReactNative = { plugins: [ - devExpressionWithCodes, // this pass has to run before `rewrite-modules` - [babelPluginModules, {map: moduleMapReactNative}], + [babelPluginModules, {map: moduleMapReactNative, 'prefix': ''}], ], }; @@ -227,42 +226,10 @@ gulp.task('react:clean', function() { }); gulp.task('react:modules', function() { - return merge( - gulp - .src(paths.react.src) - .pipe(babel(babelOptsReact)) - .pipe(stripProvidesModule()) - .pipe(flatten()) - .pipe(gulp.dest(paths.react.lib)), - - gulp - .src(paths.reactDOM.src) - .pipe(babel(babelOptsReactDOM)) - .pipe(stripProvidesModule()) - .pipe(flatten()) - .pipe(gulp.dest(paths.reactDOM.lib)), - - gulp - .src(paths.reactNative.src) - .pipe(babel(babelOptsReactNative)) - .pipe(stripProvidesModule()) - .pipe(flatten()) - .pipe(gulp.dest(paths.reactNative.lib)), - - gulp - .src(paths.reactTestRenderer.src) - .pipe(stripProvidesModule()) - .pipe(babel(babelOptsReactTestRenderer)) - .pipe(flatten()) - .pipe(gulp.dest(paths.reactTestRenderer.lib)), - - gulp - .src(paths.reactNoopRenderer.src) - .pipe(stripProvidesModule()) - .pipe(babel(babelOptsReactNoopRenderer)) - .pipe(flatten()) - .pipe(gulp.dest(paths.reactNoopRenderer.lib)) - ); + return gulp + .src(paths.reactNative.src, { base: '.' }) + .pipe(babel(babelOptsReactNative)) + .pipe(gulp.dest(paths.reactNative.lib)); }); gulp.task('react:extract-errors', function() { From 60cc310a04fe49fd7be4a8e295f063b0b6618889 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 12:14:08 +0100 Subject: [PATCH 10/19] remove subviews when container updates --- src/renderers/native/fiber/ReactNativeFiber.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 36d3914cefbb..a1442a52c684 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -69,7 +69,7 @@ var NativeRenderer = ReactFiberReconciler({ if (children && children.parentTag !== container) { const tempNode = { _rootNodeID: 0, children: [] }; recursivelyAppendChildren(tempNode, children); - // clear current children UIManager.removeSubview + UIManager.removeSubviewsFromContainerWithID(container); UIManager.setChildren(container, tempNode.children); children.parentTag = container; } From 7694ea6646ee1e129e50c5d90267a03eb317f3e2 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 16:09:28 +0100 Subject: [PATCH 11/19] separate stack from shared and fiber --- .../{InitializeJavaScriptAppEngine.js => InitializeCore.js} | 0 src/renderers/native/{ => shared}/NativeMethodsMixin.js | 0 src/renderers/native/{ => shared}/ReactNativeAttributePayload.js | 0 src/renderers/native/{ => shared}/ReactNativeComponentTree.js | 0 src/renderers/native/{ => shared}/ReactNativeContainerInfo.js | 0 src/renderers/native/{ => shared}/ReactNativeEventEmitter.js | 0 src/renderers/native/{ => shared}/ReactNativeFeatureFlags.js | 0 .../native/{ => shared}/ReactNativeGlobalResponderHandler.js | 0 src/renderers/native/{ => shared}/ReactNativeInjection.js | 0 src/renderers/native/{ => shared}/ReactNativePropRegistry.js | 0 src/renderers/native/{ => shared}/ReactNativeTagHandles.js | 0 .../{ => shared}/__tests__/ReactNativeAttributePayload-test.js | 0 .../native/{ => shared}/__tests__/ReactNativeEvents-test.js | 0 .../native/{ => shared}/createReactNativeComponentClass.js | 0 .../{ => shared/eventPlugins}/ReactNativeBridgeEventPlugin.js | 0 .../{ => shared/eventPlugins}/ReactNativeEventPluginOrder.js | 0 src/renderers/native/{ => shared}/findNodeHandle.js | 0 src/renderers/native/{ => stack}/ReactNativeBaseComponent.js | 0 .../native/{ => stack}/ReactNativeComponentEnvironment.js | 0 src/renderers/native/{ => stack}/ReactNativeDOMIDOperations.js | 0 src/renderers/native/{ => stack}/ReactNativeMount.js | 0 .../native/{ => stack}/ReactNativeReconcileTransaction.js | 0 src/renderers/native/{ => stack}/ReactNativeStack.js | 0 src/renderers/native/{ => stack}/ReactNativeStackInjection.js | 0 src/renderers/native/{ => stack}/ReactNativeTextComponent.js | 0 .../native/{ => stack}/__tests__/ReactNativeMount-test.js | 0 26 files changed, 0 insertions(+), 0 deletions(-) rename src/renderers/native/__mocks__/{InitializeJavaScriptAppEngine.js => InitializeCore.js} (100%) rename src/renderers/native/{ => shared}/NativeMethodsMixin.js (100%) rename src/renderers/native/{ => shared}/ReactNativeAttributePayload.js (100%) rename src/renderers/native/{ => shared}/ReactNativeComponentTree.js (100%) rename src/renderers/native/{ => shared}/ReactNativeContainerInfo.js (100%) rename src/renderers/native/{ => shared}/ReactNativeEventEmitter.js (100%) rename src/renderers/native/{ => shared}/ReactNativeFeatureFlags.js (100%) rename src/renderers/native/{ => shared}/ReactNativeGlobalResponderHandler.js (100%) rename src/renderers/native/{ => shared}/ReactNativeInjection.js (100%) rename src/renderers/native/{ => shared}/ReactNativePropRegistry.js (100%) rename src/renderers/native/{ => shared}/ReactNativeTagHandles.js (100%) rename src/renderers/native/{ => shared}/__tests__/ReactNativeAttributePayload-test.js (100%) rename src/renderers/native/{ => shared}/__tests__/ReactNativeEvents-test.js (100%) rename src/renderers/native/{ => shared}/createReactNativeComponentClass.js (100%) rename src/renderers/native/{ => shared/eventPlugins}/ReactNativeBridgeEventPlugin.js (100%) rename src/renderers/native/{ => shared/eventPlugins}/ReactNativeEventPluginOrder.js (100%) rename src/renderers/native/{ => shared}/findNodeHandle.js (100%) rename src/renderers/native/{ => stack}/ReactNativeBaseComponent.js (100%) rename src/renderers/native/{ => stack}/ReactNativeComponentEnvironment.js (100%) rename src/renderers/native/{ => stack}/ReactNativeDOMIDOperations.js (100%) rename src/renderers/native/{ => stack}/ReactNativeMount.js (100%) rename src/renderers/native/{ => stack}/ReactNativeReconcileTransaction.js (100%) rename src/renderers/native/{ => stack}/ReactNativeStack.js (100%) rename src/renderers/native/{ => stack}/ReactNativeStackInjection.js (100%) rename src/renderers/native/{ => stack}/ReactNativeTextComponent.js (100%) rename src/renderers/native/{ => stack}/__tests__/ReactNativeMount-test.js (100%) diff --git a/src/renderers/native/__mocks__/InitializeJavaScriptAppEngine.js b/src/renderers/native/__mocks__/InitializeCore.js similarity index 100% rename from src/renderers/native/__mocks__/InitializeJavaScriptAppEngine.js rename to src/renderers/native/__mocks__/InitializeCore.js diff --git a/src/renderers/native/NativeMethodsMixin.js b/src/renderers/native/shared/NativeMethodsMixin.js similarity index 100% rename from src/renderers/native/NativeMethodsMixin.js rename to src/renderers/native/shared/NativeMethodsMixin.js diff --git a/src/renderers/native/ReactNativeAttributePayload.js b/src/renderers/native/shared/ReactNativeAttributePayload.js similarity index 100% rename from src/renderers/native/ReactNativeAttributePayload.js rename to src/renderers/native/shared/ReactNativeAttributePayload.js diff --git a/src/renderers/native/ReactNativeComponentTree.js b/src/renderers/native/shared/ReactNativeComponentTree.js similarity index 100% rename from src/renderers/native/ReactNativeComponentTree.js rename to src/renderers/native/shared/ReactNativeComponentTree.js diff --git a/src/renderers/native/ReactNativeContainerInfo.js b/src/renderers/native/shared/ReactNativeContainerInfo.js similarity index 100% rename from src/renderers/native/ReactNativeContainerInfo.js rename to src/renderers/native/shared/ReactNativeContainerInfo.js diff --git a/src/renderers/native/ReactNativeEventEmitter.js b/src/renderers/native/shared/ReactNativeEventEmitter.js similarity index 100% rename from src/renderers/native/ReactNativeEventEmitter.js rename to src/renderers/native/shared/ReactNativeEventEmitter.js diff --git a/src/renderers/native/ReactNativeFeatureFlags.js b/src/renderers/native/shared/ReactNativeFeatureFlags.js similarity index 100% rename from src/renderers/native/ReactNativeFeatureFlags.js rename to src/renderers/native/shared/ReactNativeFeatureFlags.js diff --git a/src/renderers/native/ReactNativeGlobalResponderHandler.js b/src/renderers/native/shared/ReactNativeGlobalResponderHandler.js similarity index 100% rename from src/renderers/native/ReactNativeGlobalResponderHandler.js rename to src/renderers/native/shared/ReactNativeGlobalResponderHandler.js diff --git a/src/renderers/native/ReactNativeInjection.js b/src/renderers/native/shared/ReactNativeInjection.js similarity index 100% rename from src/renderers/native/ReactNativeInjection.js rename to src/renderers/native/shared/ReactNativeInjection.js diff --git a/src/renderers/native/ReactNativePropRegistry.js b/src/renderers/native/shared/ReactNativePropRegistry.js similarity index 100% rename from src/renderers/native/ReactNativePropRegistry.js rename to src/renderers/native/shared/ReactNativePropRegistry.js diff --git a/src/renderers/native/ReactNativeTagHandles.js b/src/renderers/native/shared/ReactNativeTagHandles.js similarity index 100% rename from src/renderers/native/ReactNativeTagHandles.js rename to src/renderers/native/shared/ReactNativeTagHandles.js diff --git a/src/renderers/native/__tests__/ReactNativeAttributePayload-test.js b/src/renderers/native/shared/__tests__/ReactNativeAttributePayload-test.js similarity index 100% rename from src/renderers/native/__tests__/ReactNativeAttributePayload-test.js rename to src/renderers/native/shared/__tests__/ReactNativeAttributePayload-test.js diff --git a/src/renderers/native/__tests__/ReactNativeEvents-test.js b/src/renderers/native/shared/__tests__/ReactNativeEvents-test.js similarity index 100% rename from src/renderers/native/__tests__/ReactNativeEvents-test.js rename to src/renderers/native/shared/__tests__/ReactNativeEvents-test.js diff --git a/src/renderers/native/createReactNativeComponentClass.js b/src/renderers/native/shared/createReactNativeComponentClass.js similarity index 100% rename from src/renderers/native/createReactNativeComponentClass.js rename to src/renderers/native/shared/createReactNativeComponentClass.js diff --git a/src/renderers/native/ReactNativeBridgeEventPlugin.js b/src/renderers/native/shared/eventPlugins/ReactNativeBridgeEventPlugin.js similarity index 100% rename from src/renderers/native/ReactNativeBridgeEventPlugin.js rename to src/renderers/native/shared/eventPlugins/ReactNativeBridgeEventPlugin.js diff --git a/src/renderers/native/ReactNativeEventPluginOrder.js b/src/renderers/native/shared/eventPlugins/ReactNativeEventPluginOrder.js similarity index 100% rename from src/renderers/native/ReactNativeEventPluginOrder.js rename to src/renderers/native/shared/eventPlugins/ReactNativeEventPluginOrder.js diff --git a/src/renderers/native/findNodeHandle.js b/src/renderers/native/shared/findNodeHandle.js similarity index 100% rename from src/renderers/native/findNodeHandle.js rename to src/renderers/native/shared/findNodeHandle.js diff --git a/src/renderers/native/ReactNativeBaseComponent.js b/src/renderers/native/stack/ReactNativeBaseComponent.js similarity index 100% rename from src/renderers/native/ReactNativeBaseComponent.js rename to src/renderers/native/stack/ReactNativeBaseComponent.js diff --git a/src/renderers/native/ReactNativeComponentEnvironment.js b/src/renderers/native/stack/ReactNativeComponentEnvironment.js similarity index 100% rename from src/renderers/native/ReactNativeComponentEnvironment.js rename to src/renderers/native/stack/ReactNativeComponentEnvironment.js diff --git a/src/renderers/native/ReactNativeDOMIDOperations.js b/src/renderers/native/stack/ReactNativeDOMIDOperations.js similarity index 100% rename from src/renderers/native/ReactNativeDOMIDOperations.js rename to src/renderers/native/stack/ReactNativeDOMIDOperations.js diff --git a/src/renderers/native/ReactNativeMount.js b/src/renderers/native/stack/ReactNativeMount.js similarity index 100% rename from src/renderers/native/ReactNativeMount.js rename to src/renderers/native/stack/ReactNativeMount.js diff --git a/src/renderers/native/ReactNativeReconcileTransaction.js b/src/renderers/native/stack/ReactNativeReconcileTransaction.js similarity index 100% rename from src/renderers/native/ReactNativeReconcileTransaction.js rename to src/renderers/native/stack/ReactNativeReconcileTransaction.js diff --git a/src/renderers/native/ReactNativeStack.js b/src/renderers/native/stack/ReactNativeStack.js similarity index 100% rename from src/renderers/native/ReactNativeStack.js rename to src/renderers/native/stack/ReactNativeStack.js diff --git a/src/renderers/native/ReactNativeStackInjection.js b/src/renderers/native/stack/ReactNativeStackInjection.js similarity index 100% rename from src/renderers/native/ReactNativeStackInjection.js rename to src/renderers/native/stack/ReactNativeStackInjection.js diff --git a/src/renderers/native/ReactNativeTextComponent.js b/src/renderers/native/stack/ReactNativeTextComponent.js similarity index 100% rename from src/renderers/native/ReactNativeTextComponent.js rename to src/renderers/native/stack/ReactNativeTextComponent.js diff --git a/src/renderers/native/__tests__/ReactNativeMount-test.js b/src/renderers/native/stack/__tests__/ReactNativeMount-test.js similarity index 100% rename from src/renderers/native/__tests__/ReactNativeMount-test.js rename to src/renderers/native/stack/__tests__/ReactNativeMount-test.js From d813f1e713d0b4e8cf73c1724b9c365a7a10a922 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 16:37:25 +0100 Subject: [PATCH 12/19] add container info and remove parentTag --- .../native/fiber/ReactNativeFiber.js | 29 ++++++++++++------- .../native/shared/ReactNativeContainerInfo.js | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index a1442a52c684..d70f7c112e6c 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -19,6 +19,7 @@ var ReactFiberReconciler = require('ReactFiberReconciler'); var ReactNativeComponentTree = require('ReactNativeComponentTree'); var ReactNativeTagHandles = require('ReactNativeTagHandles'); var ReactNativeInjection = require('ReactNativeInjection'); +var ReactNativeContainerInfo = require('ReactNativeContainerInfo'); var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); var UIManager = require('UIManager'); var findNodeHandle = require('findNodeHandle'); @@ -34,17 +35,17 @@ findNodeHandle._injectFiber(function(fiber: Fiber) { }); type Container = { + children: Array; _tag: number; }; + type Props = { [key: any]: any }; type Instance = { - parentTag?: number; _rootNodeID: number; children: Array; }; type TextInstance = { - parentTag?: number; _rootNodeID: number; }; @@ -65,13 +66,17 @@ function recursivelyAppendChildren(parent : Instance, child : HostChildren) : void { - if (children && children.parentTag !== container) { - const tempNode = { _rootNodeID: 0, children: [] }; - recursivelyAppendChildren(tempNode, children); - UIManager.removeSubviewsFromContainerWithID(container); - UIManager.setChildren(container, tempNode.children); - children.parentTag = container; + updateContainer(container: Container, children : HostChildren) : void { + const tempNode = { _rootNodeID: 0, children: [] }; + recursivelyAppendChildren(tempNode, children); + const shouldUpdate = !( + container.children.length === tempNode.children.length && container.children.every((tag, i) => tempNode.children[i] === tag) + ); + if (shouldUpdate) { + container.children = tempNode.children; + // TODO: what if some views only was moved? + UIManager.removeSubviewsFromContainerWithID(container._tag); + UIManager.setChildren(container._tag, tempNode.children); } }, @@ -234,7 +239,11 @@ var ReactNative = { let root; if (!_instancesByContainerID[containerTag]) { - root = NativeRenderer.mountContainer(element, containerTag, callback); + root = NativeRenderer.mountContainer( + element, + ReactNativeContainerInfo(containerTag), + callback + ); } else { NativeRenderer.updateContainer( element, diff --git a/src/renderers/native/shared/ReactNativeContainerInfo.js b/src/renderers/native/shared/ReactNativeContainerInfo.js index 6d24f4c75156..f46252079399 100644 --- a/src/renderers/native/shared/ReactNativeContainerInfo.js +++ b/src/renderers/native/shared/ReactNativeContainerInfo.js @@ -14,6 +14,7 @@ function ReactNativeContainerInfo(tag: number) { var info = { _tag: tag, + children: [], }; return info; } From 257cdbf687e4c7edd9f70c1be070a9784f411653 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 17:01:35 +0100 Subject: [PATCH 13/19] support singlechild and multichild transition --- .../native/fiber/ReactNativeFiber.js | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index d70f7c112e6c..1f923c4d956f 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -44,6 +44,7 @@ type Props = { [key: any]: any }; type Instance = { _rootNodeID: number; children: Array; + child: ?number; }; type TextInstance = { _rootNodeID: number; @@ -91,23 +92,26 @@ var NativeRenderer = ReactFiberReconciler({ const node = { _rootNodeID: ReactNativeTagHandles.allocateTag(), children: [], + child: null, }; precacheFiberNode(internalInstanceHandle, node._rootNodeID); + const attributes = ReactNativeAttributePayload.create( + props, + viewConfig.validAttributes + ); + UIManager.createView(node._rootNodeID, type, root, attributes); + if (typeof props.children === 'string' || typeof props.children === 'number') { // create text node const textTag = ReactNativeTagHandles.allocateTag(); const text = '' + props.children; UIManager.createView(textTag, 'RCTRawText', root, { text: text }); + node.child = textTag; node.children.push(textTag); + } else { + recursivelyAppendChildren(node, children); } - recursivelyAppendChildren(node, children); - const attributes = ReactNativeAttributePayload.create( - props, - viewConfig.validAttributes - ); - - UIManager.createView(node._rootNodeID, type, root, attributes); UIManager.setChildren(node._rootNodeID, node.children.slice()); return node; }, @@ -125,16 +129,28 @@ var NativeRenderer = ReactFiberReconciler({ const { viewConfig } = internalInstanceHandle.return.type; const oldChildren = oldProps.children; const newChildren = newProps.children; - if (typeof oldChildren === 'string' || oldChildren === 'number') { - const textTag = node.children[0]; + if (typeof oldChildren === 'string' || typeof oldChildren === 'number') { + const textTag = node.child; if (typeof newChildren === 'string' || typeof newChildren === 'number') { + // singlechild -> singlechild UIManager.updateView(textTag, 'RCTRawText', { text: '' + newChildren }); - } else { - // TODO: does this work? - // will appendChild/insertBefore be called before this? if so this won't work - UIManager.manageChildren(node._rootNodeID, [], [], [], [], [0]); - node.children.splice(0, 1); + } else if (textTag) { + // singlechild -> multichild + const index = node.children.indexOf(textTag); + UIManager.manageChildren(node._rootNodeID, [], [], [], [], [index]); + node.children.splice(index, 1); + node.child = null; } + } else if ( + typeof newChildren === 'string' || + typeof newChildren === 'number') { + // multichild -> singlechild + const textTag = ReactNativeTagHandles.allocateTag(); + const text = '' + newChildren; + UIManager.createView(textTag, 'RCTRawText', root, { text: text }); + node.child = textTag; + node.children.push(textTag); + UIManager.setChildren(node._rootNodeID, node.children.slice()); } const updatePayload = ReactNativeAttributePayload.diff( From 8d558bda2e075d42ddb0249889209fd7fbe05b22 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 17:41:13 +0100 Subject: [PATCH 14/19] support list reordering and insertBefore --- .../native/fiber/ReactNativeFiber.js | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 1f923c4d956f..716bac059389 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -101,7 +101,6 @@ var NativeRenderer = ReactFiberReconciler({ viewConfig.validAttributes ); UIManager.createView(node._rootNodeID, type, root, attributes); - if (typeof props.children === 'string' || typeof props.children === 'number') { // create text node const textTag = ReactNativeTagHandles.allocateTag(); @@ -187,24 +186,56 @@ var NativeRenderer = ReactFiberReconciler({ } }, - appendChild(parentInstance : Instance, child : Instance | TextInstance) : void { - parentInstance.children.push(child._rootNodeID); - UIManager.manageChildren( - parentInstance._rootNodeID, - [], - [], - [child._rootNodeID], - [parentInstance.children.length - 1 ], - [] - ); + appendChild(parent: Instance, child : Instance | TextInstance) : void { + if (parent.children.includes(child._rootNodeID)) { + // TODO: is this really needed? + // this is needed when changing order of a list like this: + // ['Hello', 'Foo', 'Bar'] -> ['Bar', 'Foo', 'Hello'] + const children = parent.children; + const childIndex = children.indexOf(child._rootNodeID); + children.splice(childIndex, 1); + children.push(child._rootNodeID); + UIManager.manageChildren( + parent._rootNodeID, + [childIndex], + [children.length - 1], + [], + [], + [] + ); + } else { + parent.children.push(child._rootNodeID); + UIManager.manageChildren( + parent._rootNodeID, + [], + [], + [child._rootNodeID], + [parent.children.length - 1 ], + [] + ); + } }, insertBefore( - parentInstance : Instance, + parent : Instance, child : Instance | TextInstance, beforeChild : Instance | TextInstance - ) : void { - console.log('insert before'); + ) : void {; + const children = parent.children; + const beforeIndex = children.indexOf(beforeChild._rootNodeID) - 1; + const childIndex = children.indexOf(child._rootNodeID); + console.log(children); + children.splice(childIndex, 1); + children.splice(beforeIndex, 0, child._rootNodeID); + console.log(children); + UIManager.manageChildren( + parent._rootNodeID, + [childIndex], + [beforeIndex], + [], + [], + [] + ); }, removeChild(parentInstance : Instance, child : Instance | TextInstance) : void { From fbd678c1b8b47a6bd4478e74dbc9285d2ba85572 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Wed, 23 Nov 2016 17:48:35 +0100 Subject: [PATCH 15/19] Remove some logging --- src/renderers/native/fiber/ReactNativeFiber.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 716bac059389..c4089a00de27 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -224,10 +224,8 @@ var NativeRenderer = ReactFiberReconciler({ const children = parent.children; const beforeIndex = children.indexOf(beforeChild._rootNodeID) - 1; const childIndex = children.indexOf(child._rootNodeID); - console.log(children); children.splice(childIndex, 1); children.splice(beforeIndex, 0, child._rootNodeID); - console.log(children); UIManager.manageChildren( parent._rootNodeID, [childIndex], @@ -298,8 +296,6 @@ var ReactNative = { callback ); } - // console.log(root); - // is this correct? _instancesByContainerID[containerTag] = root; return NativeRenderer.getPublicRootInstance(root); From 0700a88864af6a34a4749bd412c652f85d25da64 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Thu, 24 Nov 2016 15:51:47 +0100 Subject: [PATCH 16/19] remove updateContainer and some DRY --- .../native/fiber/ReactNativeFiber.js | 86 ++++++++----------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index c4089a00de27..13f6976fcc6d 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -19,7 +19,6 @@ var ReactFiberReconciler = require('ReactFiberReconciler'); var ReactNativeComponentTree = require('ReactNativeComponentTree'); var ReactNativeTagHandles = require('ReactNativeTagHandles'); var ReactNativeInjection = require('ReactNativeInjection'); -var ReactNativeContainerInfo = require('ReactNativeContainerInfo'); var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); var UIManager = require('UIManager'); var findNodeHandle = require('findNodeHandle'); @@ -36,7 +35,7 @@ findNodeHandle._injectFiber(function(fiber: Fiber) { type Container = { children: Array; - _tag: number; + _rootNodeID: number; }; type Props = { [key: any]: any }; @@ -50,41 +49,20 @@ type TextInstance = { _rootNodeID: number; }; -function recursivelyAppendChildren(parent : Instance, child : HostChildren) { - if (!child) { - return; - } - if (typeof child._rootNodeID === 'number') { - parent.children.push(child._rootNodeID); - } else { - /* As a result of the refinement issue this type isn't known. */ - let node : any = child; - do { - recursivelyAppendChildren(parent, node.output); - } while (node = node.sibling); - } +function isTextChild(maybeTextChild: any) { + return typeof maybeTextChild === 'string' || typeof maybeTextChild === 'number'; } var NativeRenderer = ReactFiberReconciler({ + prepareForCommit() { - updateContainer(container: Container, children : HostChildren) : void { - const tempNode = { _rootNodeID: 0, children: [] }; - recursivelyAppendChildren(tempNode, children); - const shouldUpdate = !( - container.children.length === tempNode.children.length && container.children.every((tag, i) => tempNode.children[i] === tag) - ); - if (shouldUpdate) { - container.children = tempNode.children; - // TODO: what if some views only was moved? - UIManager.removeSubviewsFromContainerWithID(container._tag); - UIManager.setChildren(container._tag, tempNode.children); - } }, + resetAfterCommit() { + }, createInstance( type : string, props : Props, - children : HostChildren, internalInstanceHandle : Object ) : Instance { const { viewConfig } = internalInstanceHandle.return.type; @@ -96,22 +74,21 @@ var NativeRenderer = ReactFiberReconciler({ }; precacheFiberNode(internalInstanceHandle, node._rootNodeID); - const attributes = ReactNativeAttributePayload.create( - props, - viewConfig.validAttributes - ); - UIManager.createView(node._rootNodeID, type, root, attributes); - if (typeof props.children === 'string' || typeof props.children === 'number') { + if (isTextChild(props.children)) { // create text node const textTag = ReactNativeTagHandles.allocateTag(); const text = '' + props.children; UIManager.createView(textTag, 'RCTRawText', root, { text: text }); node.child = textTag; node.children.push(textTag); - } else { - recursivelyAppendChildren(node, children); } - UIManager.setChildren(node._rootNodeID, node.children.slice()); + + const attributes = ReactNativeAttributePayload.create( + props, + viewConfig.validAttributes + ); + UIManager.createView(node._rootNodeID, type, root, attributes); + return node; }, @@ -128,9 +105,9 @@ var NativeRenderer = ReactFiberReconciler({ const { viewConfig } = internalInstanceHandle.return.type; const oldChildren = oldProps.children; const newChildren = newProps.children; - if (typeof oldChildren === 'string' || typeof oldChildren === 'number') { + if (isTextChild(oldChildren)) { const textTag = node.child; - if (typeof newChildren === 'string' || typeof newChildren === 'number') { + if (isTextChild(newChildren)) { // singlechild -> singlechild UIManager.updateView(textTag, 'RCTRawText', { text: '' + newChildren }); } else if (textTag) { @@ -140,9 +117,7 @@ var NativeRenderer = ReactFiberReconciler({ node.children.splice(index, 1); node.child = null; } - } else if ( - typeof newChildren === 'string' || - typeof newChildren === 'number') { + } else if (isTextChild(newChildren)) { // multichild -> singlechild const textTag = ReactNativeTagHandles.allocateTag(); const text = '' + newChildren; @@ -186,11 +161,17 @@ var NativeRenderer = ReactFiberReconciler({ } }, - appendChild(parent: Instance, child : Instance | TextInstance) : void { + appendInitialChild(parent: Instance, child: Instance | TextInstance): void { + parent.children.push(child._rootNodeID); + }, + + finalizeInitialChildren(node: Instance, type: string, props: Props): void { + UIManager.setChildren(node._rootNodeID, node.children.slice()); + }, + + appendChild(parent: Instance | Container, child : Instance | TextInstance) : void { if (parent.children.includes(child._rootNodeID)) { - // TODO: is this really needed? - // this is needed when changing order of a list like this: - // ['Hello', 'Foo', 'Bar'] -> ['Bar', 'Foo', 'Hello'] + // appendChild is called when a item get's moved to the end of a list const children = parent.children; const childIndex = children.indexOf(child._rootNodeID); children.splice(childIndex, 1); @@ -217,7 +198,7 @@ var NativeRenderer = ReactFiberReconciler({ }, insertBefore( - parent : Instance, + parent : Instance | Container, child : Instance | TextInstance, beforeChild : Instance | TextInstance ) : void {; @@ -236,11 +217,11 @@ var NativeRenderer = ReactFiberReconciler({ ); }, - removeChild(parentInstance : Instance, child : Instance | TextInstance) : void { - const childIndex = parentInstance.children.indexOf(child._rootNodeID); - parentInstance.children.splice(childIndex, 1); + removeChild(parent : Instance | Container, child : Instance | TextInstance) : void { + const childIndex = parent.children.indexOf(child._rootNodeID); + parent.children.splice(childIndex, 1); UIManager.manageChildren( - parentInstance._rootNodeID, + parent._rootNodeID, [], [], [], @@ -284,9 +265,10 @@ var ReactNative = { let root; if (!_instancesByContainerID[containerTag]) { + UIManager.removeSubviewsFromContainerWithID(containerTag); root = NativeRenderer.mountContainer( element, - ReactNativeContainerInfo(containerTag), + { _rootNodeID: containerTag, children: [] }, callback ); } else { From bd5ace227a9b7c7a20fffd3904e5e577f34e9ab3 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Thu, 24 Nov 2016 15:52:25 +0100 Subject: [PATCH 17/19] move ReactNativeContainerInfo to stack --- .../native/{shared => stack}/ReactNativeContainerInfo.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename src/renderers/native/{shared => stack}/ReactNativeContainerInfo.js (93%) diff --git a/src/renderers/native/shared/ReactNativeContainerInfo.js b/src/renderers/native/stack/ReactNativeContainerInfo.js similarity index 93% rename from src/renderers/native/shared/ReactNativeContainerInfo.js rename to src/renderers/native/stack/ReactNativeContainerInfo.js index f46252079399..f4caef57d557 100644 --- a/src/renderers/native/shared/ReactNativeContainerInfo.js +++ b/src/renderers/native/stack/ReactNativeContainerInfo.js @@ -13,8 +13,7 @@ function ReactNativeContainerInfo(tag: number) { var info = { - _tag: tag, - children: [], + _tag: tag }; return info; } From 076f9ea206a00e535565ec8c845c7deacb762617 Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Thu, 24 Nov 2016 17:31:24 +0100 Subject: [PATCH 18/19] move insertions, deletions, updates and deletions to ReactNativeFiberComponent --- .../native/fiber/ReactNativeFiber.js | 171 +++++++++--------- .../native/fiber/ReactNativeFiberComponent.js | 141 +++++++++++++++ 2 files changed, 223 insertions(+), 89 deletions(-) create mode 100644 src/renderers/native/fiber/ReactNativeFiberComponent.js diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js index 13f6976fcc6d..08b11156372d 100644 --- a/src/renderers/native/fiber/ReactNativeFiber.js +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -14,6 +14,13 @@ import type { Fiber } from 'ReactFiber'; import type { HostChildren } from 'ReactFiberReconciler'; +import type { + Instance, + TextInstance, + Container, + Props, +} from 'ReactNativeFiberComponent'; + var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); var ReactFiberReconciler = require('ReactFiberReconciler'); var ReactNativeComponentTree = require('ReactNativeComponentTree'); @@ -22,9 +29,22 @@ var ReactNativeInjection = require('ReactNativeInjection'); var ReactNativeAttributePayload = require('ReactNativeAttributePayload'); var UIManager = require('UIManager'); var findNodeHandle = require('findNodeHandle'); +var ReactNativeFiberComponent = require('ReactNativeFiberComponent'); + var warning = require('warning'); var { precacheFiberNode } = ReactNativeComponentTree; +var { + createSingleChild, + updateSingleChild, + createInstance, + createTextInstance, + updateTextInstance, + appendChild, + moveChild, + insertBefore, + removeChild, +} = ReactNativeFiberComponent; ReactNativeInjection.inject(); @@ -33,24 +53,8 @@ findNodeHandle._injectFiber(function(fiber: Fiber) { return inst ? inst._rootNodeID : null; }); -type Container = { - children: Array; - _rootNodeID: number; -}; - -type Props = { [key: any]: any }; - -type Instance = { - _rootNodeID: number; - children: Array; - child: ?number; -}; -type TextInstance = { - _rootNodeID: number; -}; - -function isTextChild(maybeTextChild: any) { - return typeof maybeTextChild === 'string' || typeof maybeTextChild === 'number'; +function isText(maybeText: any) { + return typeof maybeText === 'string' || typeof maybeText === 'number'; } var NativeRenderer = ReactFiberReconciler({ @@ -67,27 +71,17 @@ var NativeRenderer = ReactFiberReconciler({ ) : Instance { const { viewConfig } = internalInstanceHandle.return.type; const root = 1; // HACK - const node = { - _rootNodeID: ReactNativeTagHandles.allocateTag(), - children: [], - child: null, - }; - precacheFiberNode(internalInstanceHandle, node._rootNodeID); - - if (isTextChild(props.children)) { - // create text node - const textTag = ReactNativeTagHandles.allocateTag(); - const text = '' + props.children; - UIManager.createView(textTag, 'RCTRawText', root, { text: text }); - node.child = textTag; - node.children.push(textTag); - } - const attributes = ReactNativeAttributePayload.create( props, viewConfig.validAttributes ); - UIManager.createView(node._rootNodeID, type, root, attributes); + + const node = createInstance(type, attributes, root); + precacheFiberNode(internalInstanceHandle, node._rootNodeID); + + if (isText(props.children)) { + createSingleChild(node, '' + props.children, root); + } return node; }, @@ -100,30 +94,24 @@ var NativeRenderer = ReactFiberReconciler({ return true; }, - commitUpdate(node : Instance, oldProps : Props, newProps : Props, internalInstanceHandle: Object) : void { + commitUpdate( + node : Instance, + oldProps : Props, + newProps : Props, + internalInstanceHandle: Object + ) : void { var root = 1; // HACK const { viewConfig } = internalInstanceHandle.return.type; const oldChildren = oldProps.children; const newChildren = newProps.children; - if (isTextChild(oldChildren)) { - const textTag = node.child; - if (isTextChild(newChildren)) { - // singlechild -> singlechild - UIManager.updateView(textTag, 'RCTRawText', { text: '' + newChildren }); - } else if (textTag) { - // singlechild -> multichild - const index = node.children.indexOf(textTag); - UIManager.manageChildren(node._rootNodeID, [], [], [], [], [index]); - node.children.splice(index, 1); - node.child = null; - } - } else if (isTextChild(newChildren)) { - // multichild -> singlechild - const textTag = ReactNativeTagHandles.allocateTag(); - const text = '' + newChildren; - UIManager.createView(textTag, 'RCTRawText', root, { text: text }); - node.child = textTag; - node.children.push(textTag); + + // Single child handling + if (node.child) { + // handles text updates and deletion of single child + updateSingleChild(node, newChildren); + } else if (isText(newChildren)) { + // handles creation of single child + createSingleChild(node, '' + newChildren, root); UIManager.setChildren(node._rootNodeID, node.children.slice()); } @@ -132,6 +120,7 @@ var NativeRenderer = ReactFiberReconciler({ newProps, viewConfig.validAttributes ); + if (updatePayload) { UIManager.updateView( node._rootNodeID, @@ -141,57 +130,63 @@ var NativeRenderer = ReactFiberReconciler({ } }, - createTextInstance(text: string, internalInstanceHandle : Object) : TextInstance { + createTextInstance( + text: string, + internalInstanceHandle : Object + ) : TextInstance { const root = 1; // HACK - const node : TextInstance = { - _rootNodeID: ReactNativeTagHandles.allocateTag(), - }; + const node = createTextInstance(text, root); precacheFiberNode(internalInstanceHandle, node._rootNodeID); - UIManager.createView(node._rootNodeID, 'RCTRawText', root, { text: text }); return node; }, - commitTextUpdate(textInstance : TextInstance, oldText : string, newText : string) : void { + commitTextUpdate( + textInstance : TextInstance, + oldText : string, + newText : string + ) : void { if (oldText !== newText) { - UIManager.updateView( - textInstance._rootNodeID, - 'RCTRawText', - { text: newText } - ); + updateTextInstance(textInstance, newText); } }, - appendInitialChild(parent: Instance, child: Instance | TextInstance): void { - parent.children.push(child._rootNodeID); + appendInitialChild( + parent: Instance, + child: Instance | TextInstance + ): void { + appendChild(parent, child); }, - finalizeInitialChildren(node: Instance, type: string, props: Props): void { + finalizeInitialChildren( + node: Instance, + type: string, + props: Props + ): void { UIManager.setChildren(node._rootNodeID, node.children.slice()); }, - appendChild(parent: Instance | Container, child : Instance | TextInstance) : void { + appendChild( + parent: Instance | Container, + child : Instance | TextInstance + ) : void { if (parent.children.includes(child._rootNodeID)) { - // appendChild is called when a item get's moved to the end of a list - const children = parent.children; - const childIndex = children.indexOf(child._rootNodeID); - children.splice(childIndex, 1); - children.push(child._rootNodeID); + const [fromIndex, toIndex] = moveChild(parent, child); UIManager.manageChildren( parent._rootNodeID, - [childIndex], - [children.length - 1], + [fromIndex], + [toIndex], [], [], [] ); } else { - parent.children.push(child._rootNodeID); + const newIndex = appendChild(parent, child); UIManager.manageChildren( parent._rootNodeID, [], [], [child._rootNodeID], - [parent.children.length - 1 ], + [newIndex], [] ); } @@ -200,13 +195,9 @@ var NativeRenderer = ReactFiberReconciler({ insertBefore( parent : Instance | Container, child : Instance | TextInstance, - beforeChild : Instance | TextInstance + before : Instance | TextInstance ) : void {; - const children = parent.children; - const beforeIndex = children.indexOf(beforeChild._rootNodeID) - 1; - const childIndex = children.indexOf(child._rootNodeID); - children.splice(childIndex, 1); - children.splice(beforeIndex, 0, child._rootNodeID); + const [childIndex, beforeIndex] = insertBefore(parent, child, before); UIManager.manageChildren( parent._rootNodeID, [childIndex], @@ -217,16 +208,18 @@ var NativeRenderer = ReactFiberReconciler({ ); }, - removeChild(parent : Instance | Container, child : Instance | TextInstance) : void { - const childIndex = parent.children.indexOf(child._rootNodeID); - parent.children.splice(childIndex, 1); + removeChild( + parent : Instance | Container, + child : Instance | TextInstance + ) : void { + const removedIndex = removeChild(parent, child); UIManager.manageChildren( parent._rootNodeID, [], [], [], [], - [childIndex] + [removedIndex] ); }, diff --git a/src/renderers/native/fiber/ReactNativeFiberComponent.js b/src/renderers/native/fiber/ReactNativeFiberComponent.js new file mode 100644 index 000000000000..4967c9f09d72 --- /dev/null +++ b/src/renderers/native/fiber/ReactNativeFiberComponent.js @@ -0,0 +1,141 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactNativeFiberComponent + * @flow + */ + +'use strict'; + +var ReactNativeTagHandles = require('ReactNativeTagHandles'); +var UIManager = require('UIManager'); + +export type Container = { + children: Array; + _rootNodeID: number; +}; + +export type Props = { [key: any]: any }; + +export type Instance = { + _rootNodeID: number; + children: Array; + child: ?TextInstance; +}; + +export type TextInstance = { + _rootNodeID: number; +}; + +function isText(maybeText: any) { + return typeof maybeText === 'string' || typeof maybeText === 'number'; +}; + +function createSingleChild(node: Instance, children: string, root: number) { + // multichild -> singlechild + const textNode = createTextInstance(children, root); + appendChild(node, node.child = textNode); +} + +function updateSingleChild(node: Instance, children: any) { + if (isText(children) && node.child) { + // singlechild -> singlechild + updateTextInstance(node.child, '' + children); + } else if (node.child) { + // singlechild -> multichild + const removedIndex = removeChild(node, node.child); + UIManager.manageChildren( + node._rootNodeID, + [], + [], + [], + [], + [removedIndex] + ); + node.child = null; + } +} + +function createInstance( + type: string, + attributes: ?Props, + root: number +): Instance { + + const node = { + _rootNodeID: ReactNativeTagHandles.allocateTag(), + children: [], + child: null, + }; + + UIManager.createView(node._rootNodeID, type, root, attributes); + return node; +} + +function createTextInstance(text: string, root: number): TextInstance { + const tag = ReactNativeTagHandles.allocateTag(); + UIManager.createView(tag, 'RCTRawText', root, { text: text }); + return { _rootNodeID: tag }; +} + +function updateTextInstance(node: TextInstance, text: string): void { + UIManager.updateView(node._rootNodeID, 'RCTRawText', { text: text }); +} + +function appendChild( + parent: Instance | Container, + child: Instance | TextInstance +): number { + return parent.children.push(child._rootNodeID) - 1; +} + +function moveChild( + parent: Instance | Container, + child: Instance | TextInstance +): Array { + const fromIndex = parent.children.indexOf(child._rootNodeID); + parent.children.splice(fromIndex, 1); + const toIndex = parent.children.push(child._rootNodeID) - 1; + + return [fromIndex, toIndex]; +} + +function insertBefore( + parent: Instance | Container, + child: Instance | TextInstance, + before: Instance | TextInstance +): Array { + const children = parent.children; + const beforeIndex = children.indexOf(before._rootNodeID) - 1; + const childIndex = children.indexOf(child._rootNodeID); + children.splice(childIndex, 1); + children.splice(beforeIndex, 0, child._rootNodeID); + + return [childIndex, beforeIndex]; +} + +function removeChild( + parent: Instance | Container, + child: Instance | TextInstance +): number { + const childIndex = parent.children.indexOf(child._rootNodeID); + parent.children.splice(childIndex, 1); + return childIndex; +} + +module.exports = { + createSingleChild: createSingleChild, + updateSingleChild: updateSingleChild, + createInstance: createInstance, + createTextInstance: createTextInstance, + updateTextInstance: updateTextInstance, + appendChild: appendChild, + moveChild: moveChild, + insertBefore: insertBefore, + removeChild: removeChild, +} From c3303c7e76a2f58a932e9a8ee1aeddab3520dbfc Mon Sep 17 00:00:00 2001 From: Edvin Erikson Date: Thu, 24 Nov 2016 17:31:49 +0100 Subject: [PATCH 19/19] better typing for manageChildren --- flow/react-native-host-hooks.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js index b52951fcd3a3..2d5085104549 100644 --- a/flow/react-native-host-hooks.js +++ b/flow/react-native-host-hooks.js @@ -30,7 +30,14 @@ declare module 'UIManager' { declare var customBubblingEventTypes : Object; declare var customDirectEventTypes : Object; declare function createView() : void; - declare function manageChildren() : void; + declare function manageChildren( + tag: number, + moveFromIndices: Array, + moveToIndices: Array, + addChildTags: Array, + addAtIndices: Array, + removeAtIndices: Array + ) : void; declare function measure() : void; declare function measureInWindow() : void; declare function measureLayout() : void;