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/flow/react-native-host-hooks.js b/flow/react-native-host-hooks.js index 7c0a1b5c3910..2d5085104549 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; } @@ -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; 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() { 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/__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/createReactNativeComponentClass.js b/src/renderers/native/createReactNativeComponentClass.js deleted file mode 100644 index e5f113bf0bbe..000000000000 --- a/src/renderers/native/createReactNativeComponentClass.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 createReactNativeComponentClass - * @flow - */ - -'use strict'; - -var ReactNativeBaseComponent = require('ReactNativeBaseComponent'); - -// See also ReactNativeBaseComponent -type ReactNativeBaseComponentViewConfig = { - validAttributes: Object, - uiViewClassName: string, - propTypes?: Object, -} - -/** - * @param {string} config iOS View configuration. - * @private - */ -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; - - return ((Constructor: any): ReactClass); -}; - -module.exports = createReactNativeComponentClass; diff --git a/src/renderers/native/fiber/ReactNativeFiber.js b/src/renderers/native/fiber/ReactNativeFiber.js new file mode 100644 index 000000000000..08b11156372d --- /dev/null +++ b/src/renderers/native/fiber/ReactNativeFiber.js @@ -0,0 +1,296 @@ +/** + * 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'; + +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'); +var ReactNativeTagHandles = require('ReactNativeTagHandles'); +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(); + +findNodeHandle._injectFiber(function(fiber: Fiber) { + const inst = NativeRenderer.findHostInstance(fiber); + return inst ? inst._rootNodeID : null; +}); + +function isText(maybeText: any) { + return typeof maybeText === 'string' || typeof maybeText === 'number'; +} + +var NativeRenderer = ReactFiberReconciler({ + prepareForCommit() { + + }, + resetAfterCommit() { + + }, + createInstance( + type : string, + props : Props, + internalInstanceHandle : Object + ) : Instance { + const { viewConfig } = internalInstanceHandle.return.type; + const root = 1; // HACK + const attributes = ReactNativeAttributePayload.create( + props, + viewConfig.validAttributes + ); + + const node = createInstance(type, attributes, root); + precacheFiberNode(internalInstanceHandle, node._rootNodeID); + + if (isText(props.children)) { + createSingleChild(node, '' + props.children, root); + } + + 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; + + // 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()); + } + + 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 = createTextInstance(text, root); + precacheFiberNode(internalInstanceHandle, node._rootNodeID); + return node; + }, + + commitTextUpdate( + textInstance : TextInstance, + oldText : string, + newText : string + ) : void { + if (oldText !== newText) { + updateTextInstance(textInstance, newText); + } + }, + + appendInitialChild( + parent: Instance, + child: Instance | TextInstance + ): void { + appendChild(parent, child); + }, + + 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)) { + const [fromIndex, toIndex] = moveChild(parent, child); + UIManager.manageChildren( + parent._rootNodeID, + [fromIndex], + [toIndex], + [], + [], + [] + ); + } else { + const newIndex = appendChild(parent, child); + UIManager.manageChildren( + parent._rootNodeID, + [], + [], + [child._rootNodeID], + [newIndex], + [] + ); + } + }, + + insertBefore( + parent : Instance | Container, + child : Instance | TextInstance, + before : Instance | TextInstance + ) : void {; + const [childIndex, beforeIndex] = insertBefore(parent, child, before); + UIManager.manageChildren( + parent._rootNodeID, + [childIndex], + [beforeIndex], + [], + [], + [] + ); + }, + + removeChild( + parent : Instance | Container, + child : Instance | TextInstance + ) : void { + const removedIndex = removeChild(parent, child); + UIManager.manageChildren( + parent._rootNodeID, + [], + [], + [], + [], + [removedIndex] + ); + }, + + 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]) { + UIManager.removeSubviewsFromContainerWithID(containerTag); + root = NativeRenderer.mountContainer( + element, + { _rootNodeID: containerTag, children: [] }, + callback + ); + } else { + NativeRenderer.updateContainer( + element, + root = _instancesByContainerID[containerTag], + callback + ); + } + _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; 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, +} 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 90% rename from src/renderers/native/ReactNativeComponentTree.js rename to src/renderers/native/shared/ReactNativeComponentTree.js index 7b16b8512a17..27038e71d227 100644 --- a/src/renderers/native/ReactNativeComponentTree.js +++ b/src/renderers/native/shared/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; 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/shared/ReactNativeFeatureFlags.js b/src/renderers/native/shared/ReactNativeFeatureFlags.js new file mode 100644 index 000000000000..8f9e36713e60 --- /dev/null +++ b/src/renderers/native/shared/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; diff --git a/src/renderers/native/ReactNativeGlobalResponderHandler.js b/src/renderers/native/shared/ReactNativeGlobalResponderHandler.js similarity index 90% rename from src/renderers/native/ReactNativeGlobalResponderHandler.js rename to src/renderers/native/shared/ReactNativeGlobalResponderHandler.js index 84bd45fb6f1f..c0534f4202bf 100644 --- a/src/renderers/native/ReactNativeGlobalResponderHandler.js +++ b/src/renderers/native/shared/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 diff --git a/src/renderers/native/ReactNativeInjection.js b/src/renderers/native/shared/ReactNativeInjection.js similarity index 98% rename from src/renderers/native/ReactNativeInjection.js rename to src/renderers/native/shared/ReactNativeInjection.js index 16b8564b588e..d3000c8d0a43 100644 --- a/src/renderers/native/ReactNativeInjection.js +++ b/src/renderers/native/shared/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/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/shared/createReactNativeComponentClass.js b/src/renderers/native/shared/createReactNativeComponentClass.js new file mode 100644 index 000000000000..7079f7a4986c --- /dev/null +++ b/src/renderers/native/shared/createReactNativeComponentClass.js @@ -0,0 +1,67 @@ +/** + * 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 createReactNativeComponentClass + * @flow + */ + +'use strict'; + +var ReactNativeFeatureFlags = require('ReactNativeFeatureFlags'); +var ReactNativeBaseComponent = require('ReactNativeBaseComponent'); +var NativeMethodsMixin = require('NativeMethodsMixin'); +var React = require('React'); + +// See also ReactNativeBaseComponent +type ReactNativeBaseComponentViewConfig = { + validAttributes: Object, + uiViewClassName: string, + propTypes?: Object, +} + +/** + * @param {string} config iOS View configuration. + * @private + */ +var createReactNativeComponentClass = function( + viewConfig: ReactNativeBaseComponentViewConfig +): ReactClass { + 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); +}; + +module.exports = createReactNativeComponentClass; 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 88% rename from src/renderers/native/findNodeHandle.js rename to src/renderers/native/shared/findNodeHandle.js index aca064b52954..5eb3c53a2b04 100644 --- a/src/renderers/native/findNodeHandle.js +++ b/src/renderers/native/shared/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; 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/ReactNativeContainerInfo.js b/src/renderers/native/stack/ReactNativeContainerInfo.js similarity index 97% rename from src/renderers/native/ReactNativeContainerInfo.js rename to src/renderers/native/stack/ReactNativeContainerInfo.js index 6d24f4c75156..f4caef57d557 100644 --- a/src/renderers/native/ReactNativeContainerInfo.js +++ b/src/renderers/native/stack/ReactNativeContainerInfo.js @@ -13,7 +13,7 @@ function ReactNativeContainerInfo(tag: number) { var info = { - _tag: tag, + _tag: tag }; return info; } 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/stack/ReactNativeStack.js b/src/renderers/native/stack/ReactNativeStack.js new file mode 100644 index 000000000000..7d4e76a6d989 --- /dev/null +++ b/src/renderers/native/stack/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/ReactNativeStackInjection.js b/src/renderers/native/stack/ReactNativeStackInjection.js similarity index 91% rename from src/renderers/native/ReactNativeStackInjection.js rename to src/renderers/native/stack/ReactNativeStackInjection.js index 83a5bcd4d4b6..eb30c8f5de64 100644 --- a/src/renderers/native/ReactNativeStackInjection.js +++ b/src/renderers/native/stack/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'); @@ -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/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