mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Added new fiber-based renderer for native dubbed ReactNativeFiber
This commit is contained in:
27
flow/react-native-host-hooks.js
vendored
27
flow/react-native-host-hooks.js
vendored
@@ -29,16 +29,35 @@ declare module 'TextInputState' {
|
||||
declare module 'UIManager' {
|
||||
declare var customBubblingEventTypes : Object;
|
||||
declare var customDirectEventTypes : Object;
|
||||
declare function createView() : void;
|
||||
declare function manageChildren() : void;
|
||||
declare function createView(
|
||||
reactTag : number,
|
||||
viewName : string,
|
||||
rootTag : number,
|
||||
props : ?Object,
|
||||
) : void;
|
||||
declare function manageChildren(
|
||||
containerTag : number,
|
||||
moveFromIndices : Array<number>,
|
||||
moveToIndices : Array<number>,
|
||||
addChildReactTags : Array<number>,
|
||||
addAtIndices : Array<number>,
|
||||
removeAtIndices : Array<number>
|
||||
) : void;
|
||||
declare function measure() : void;
|
||||
declare function measureInWindow() : void;
|
||||
declare function measureLayout() : void;
|
||||
declare function removeRootView() : void;
|
||||
declare function removeSubviewsFromContainerWithID() : void;
|
||||
declare function replaceExistingNonRootView() : void;
|
||||
declare function setChildren() : void;
|
||||
declare function updateView() : void;
|
||||
declare function setChildren(
|
||||
containerTag : number,
|
||||
reactTags : Array<number>,
|
||||
) : void;
|
||||
declare function updateView(
|
||||
reactTag : number,
|
||||
viewName : string,
|
||||
props : ?Object,
|
||||
) : void;
|
||||
}
|
||||
declare module 'View' {
|
||||
declare var exports : typeof ReactComponent;
|
||||
|
||||
@@ -150,7 +150,7 @@ var NativeMethodsMixin = {
|
||||
);
|
||||
|
||||
UIManager.updateView(
|
||||
findNodeHandle(this),
|
||||
(findNodeHandle(this) : any),
|
||||
this.viewConfig.uiViewClassName,
|
||||
updatePayload
|
||||
);
|
||||
|
||||
@@ -11,67 +11,5 @@
|
||||
*/
|
||||
'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<any>,
|
||||
mountInto: number,
|
||||
callback?: ?(() => void)
|
||||
): ?ReactComponent<any, any, any> {
|
||||
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;
|
||||
// TODO (bvaughn) Enable Fiber experiement via ReactNativeFeatureFlags
|
||||
module.exports = require('ReactNativeStack');
|
||||
|
||||
@@ -39,6 +39,10 @@ function precacheNode(inst, tag) {
|
||||
instanceCache[tag] = nativeInst;
|
||||
}
|
||||
|
||||
function precacheFiberNode(hostInst, tag) {
|
||||
instanceCache[tag] = hostInst;
|
||||
}
|
||||
|
||||
function uncacheNode(inst) {
|
||||
var tag = inst._rootNodeID;
|
||||
if (tag) {
|
||||
@@ -46,21 +50,29 @@ function uncacheNode(inst) {
|
||||
}
|
||||
}
|
||||
|
||||
function uncacheFiberNode(tag) {
|
||||
delete instanceCache[tag];
|
||||
}
|
||||
|
||||
function getInstanceFromTag(tag) {
|
||||
return instanceCache[tag] || null;
|
||||
}
|
||||
|
||||
function getTagFromInstance(inst) {
|
||||
invariant(inst._rootNodeID, 'All native instances should have a tag.');
|
||||
return inst._rootNodeID;
|
||||
// TODO (bvaughn) Clean up once Stack is deprecated
|
||||
var tag = inst._rootNodeID || inst.stateNode._nativeTag;
|
||||
invariant(tag, 'All native instances should have a tag.');
|
||||
return tag;
|
||||
}
|
||||
|
||||
var ReactNativeComponentTree = {
|
||||
getClosestInstanceFromNode: getInstanceFromTag,
|
||||
getInstanceFromNode: getInstanceFromTag,
|
||||
getNodeFromInstance: getTagFromInstance,
|
||||
precacheNode: precacheNode,
|
||||
uncacheNode: uncacheNode,
|
||||
precacheFiberNode,
|
||||
precacheNode,
|
||||
uncacheFiberNode,
|
||||
uncacheNode,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeComponentTree;
|
||||
|
||||
18
src/renderers/native/ReactNativeFeatureFlags.js
Normal file
18
src/renderers/native/ReactNativeFeatureFlags.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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 ReactNativeFeatureFlags
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactNativeFeatureFlags = {
|
||||
useFiber: false,
|
||||
};
|
||||
|
||||
module.exports = ReactNativeFeatureFlags;
|
||||
395
src/renderers/native/ReactNativeFiber.js
Normal file
395
src/renderers/native/ReactNativeFiber.js
Normal file
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* 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 { Element } from 'React';
|
||||
import type { Fiber } from 'ReactFiber';
|
||||
import type { ReactNodeList } from 'ReactTypes';
|
||||
import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry';
|
||||
|
||||
const NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
const ReactFiberReconciler = require('ReactFiberReconciler');
|
||||
const ReactGenericBatching = require('ReactGenericBatching');
|
||||
const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
|
||||
const ReactNativeComponentTree = require('ReactNativeComponentTree');
|
||||
const ReactNativeInjection = require('ReactNativeInjection');
|
||||
const ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
|
||||
const ReactPortal = require('ReactPortal');
|
||||
const UIManager = require('UIManager');
|
||||
|
||||
const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
|
||||
const findNodeHandle = require('findNodeHandle');
|
||||
const invariant = require('invariant');
|
||||
|
||||
const { precacheFiberNode, uncacheFiberNode } = ReactNativeComponentTree;
|
||||
|
||||
ReactNativeInjection.inject();
|
||||
|
||||
type Container = number;
|
||||
type Instance = {
|
||||
_children: Array<Instance | number>,
|
||||
_nativeTag: number,
|
||||
viewConfig: ReactNativeBaseComponentViewConfig,
|
||||
};
|
||||
type Props = Object;
|
||||
type TextInstance = number;
|
||||
|
||||
function NativeHostComponent(tag, viewConfig) {
|
||||
this._nativeTag = tag;
|
||||
this._children = [];
|
||||
this.viewConfig = viewConfig;
|
||||
}
|
||||
Object.assign(NativeHostComponent.prototype, NativeMethodsMixin);
|
||||
|
||||
function recursivelyUncacheFiberNode(node : Instance | TextInstance) {
|
||||
if (typeof node === 'number') { // Leaf node (eg text)
|
||||
uncacheFiberNode(node);
|
||||
} else {
|
||||
uncacheFiberNode((node : any)._nativeTag);
|
||||
|
||||
(node : any)._children.forEach(recursivelyUncacheFiberNode);
|
||||
}
|
||||
}
|
||||
|
||||
const NativeRenderer = ReactFiberReconciler({
|
||||
appendChild(
|
||||
parentInstance : Instance | Container,
|
||||
child : Instance | TextInstance
|
||||
) : void {
|
||||
if (typeof parentInstance === 'number') {
|
||||
// Root container
|
||||
UIManager.setChildren(
|
||||
parentInstance, // containerTag
|
||||
[(child : any)._nativeTag] // reactTags
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
|
||||
children.push(child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[(child : any)._nativeTag], // addChildReactTags
|
||||
[children.length - 1], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
|
||||
if (typeof child === 'number') {
|
||||
parentInstance._children.push(child);
|
||||
} else {
|
||||
parentInstance._children.push(child);
|
||||
}
|
||||
},
|
||||
|
||||
commitTextUpdate(
|
||||
textInstance : TextInstance,
|
||||
oldText : string,
|
||||
newText : string
|
||||
) : void {
|
||||
UIManager.updateView(
|
||||
textInstance, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
{text: newText}, // props
|
||||
);
|
||||
},
|
||||
|
||||
commitUpdate(
|
||||
instance : Instance,
|
||||
type : string,
|
||||
oldProps : Props,
|
||||
newProps : Props,
|
||||
rootContainerInstance : Object,
|
||||
internalInstanceHandle : Object
|
||||
) : void {
|
||||
const viewConfig = instance.viewConfig;
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, instance._nativeTag);
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.diff(
|
||||
oldProps,
|
||||
newProps,
|
||||
viewConfig.validAttributes
|
||||
);
|
||||
|
||||
UIManager.updateView(
|
||||
(instance : any)._nativeTag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
updatePayload, // props
|
||||
);
|
||||
},
|
||||
|
||||
createInstance(
|
||||
type : string,
|
||||
props : Props,
|
||||
rootContainerInstance : Container,
|
||||
hostContext : string | null,
|
||||
internalInstanceHandle : Object
|
||||
) : Instance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
const viewConfig = ReactNativeViewConfigRegistry.get(type);
|
||||
|
||||
if (__DEV__) {
|
||||
for (let key in viewConfig.validAttributes) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
deepFreezeAndThrowOnMutationInDev(props[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatePayload = ReactNativeAttributePayload.create(
|
||||
props,
|
||||
viewConfig.validAttributes
|
||||
);
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
viewConfig.uiViewClassName, // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
updatePayload, // props
|
||||
);
|
||||
|
||||
const component = new NativeHostComponent(tag, viewConfig);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
|
||||
return component;
|
||||
},
|
||||
|
||||
createTextInstance(
|
||||
text : string,
|
||||
rootContainerInstance : Container,
|
||||
internalInstanceHandle : Object,
|
||||
) : TextInstance {
|
||||
const tag = ReactNativeTagHandles.allocateTag();
|
||||
|
||||
UIManager.createView(
|
||||
tag, // reactTag
|
||||
'RCTRawText', // viewName
|
||||
rootContainerInstance, // rootTag
|
||||
{text: text} // props
|
||||
);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
|
||||
return tag;
|
||||
},
|
||||
|
||||
finalizeInitialChildren(
|
||||
parentInstance : Instance,
|
||||
type : string,
|
||||
props : Props,
|
||||
rootContainerInstance : Container,
|
||||
) : void {
|
||||
// Map from child objects to native tags.
|
||||
// Either way we need to pass a copy of the Array to prevent it from being frozen.
|
||||
const nativeTags = parentInstance._children.map(
|
||||
(child) => typeof child === 'number'
|
||||
? child // Leaf node (eg text)
|
||||
: child._nativeTag
|
||||
);
|
||||
|
||||
UIManager.setChildren(
|
||||
parentInstance._nativeTag, // containerTag
|
||||
nativeTags // reactTags
|
||||
);
|
||||
},
|
||||
|
||||
getChildHostContext(
|
||||
parentHostContext : string | null,
|
||||
type : string
|
||||
) {
|
||||
return parentHostContext;
|
||||
},
|
||||
|
||||
insertBefore(
|
||||
parentInstance : Instance | Container,
|
||||
child : Instance | TextInstance,
|
||||
beforeChild : Instance | TextInstance
|
||||
) : void {
|
||||
// TODO (bvaughn): Remove this check when...
|
||||
// We create a wrapper object for the container in ReactNative render()
|
||||
// Or we refactor to remove wrapper objects entirely.
|
||||
// For more info on pros/cons see PR #8560 description.
|
||||
invariant(
|
||||
typeof parentInstance !== 'number',
|
||||
'Container does not support insertBefore operation',
|
||||
);
|
||||
|
||||
const children = (parentInstance : any)._children;
|
||||
|
||||
const beforeChildIndex = children.indexOf(beforeChild);
|
||||
const index = children.indexOf(child);
|
||||
|
||||
// Move existing child or add new child?
|
||||
if (index >= 0) {
|
||||
children.splice(index, 1);
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance : any)._nativeTag, // containerID
|
||||
[index], // moveFromIndices
|
||||
[beforeChildIndex], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
children.splice(beforeChildIndex, 0, child);
|
||||
|
||||
UIManager.manageChildren(
|
||||
(parentInstance : any)._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[(child : any)._nativeTag], // addChildReactTags
|
||||
[beforeChildIndex], // addAtIndices
|
||||
[], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
prepareForCommit() : void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
prepareUpdate(
|
||||
instance : Instance,
|
||||
type : string,
|
||||
oldProps : Props,
|
||||
newProps : Props
|
||||
) : boolean {
|
||||
return true;
|
||||
},
|
||||
|
||||
removeChild(
|
||||
parentInstance : Instance | Container,
|
||||
child : Instance | TextInstance
|
||||
) : void {
|
||||
recursivelyUncacheFiberNode(child);
|
||||
|
||||
if (typeof parentInstance === 'number') {
|
||||
UIManager.manageChildren(
|
||||
parentInstance, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[0], // removeAtIndices
|
||||
);
|
||||
} else {
|
||||
const children = parentInstance._children;
|
||||
const index = children.indexOf(child);
|
||||
|
||||
children.splice(index, 1);
|
||||
|
||||
UIManager.manageChildren(
|
||||
parentInstance._nativeTag, // containerID
|
||||
[], // moveFromIndices
|
||||
[], // moveToIndices
|
||||
[], // addChildReactTags
|
||||
[], // addAtIndices
|
||||
[index], // removeAtIndices
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
resetAfterCommit() : void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
resetTextContent(instance : Instance) : void {
|
||||
// Noop
|
||||
},
|
||||
|
||||
scheduleAnimationCallback: global.requestAnimationFrame,
|
||||
|
||||
scheduleDeferredCallback: global.requestIdleCallback,
|
||||
|
||||
shouldSetTextContent(props : Props) : boolean {
|
||||
// TODO (bvaughn) Revisit this decision.
|
||||
// Always returning false simplifies the createInstance() implementation,
|
||||
// But creates an additional child Fiber for raw text children.
|
||||
// No additional native views are created though.
|
||||
// It's not clear to me which is better so I'm deferring for now.
|
||||
// More context @ github.com/facebook/react/pull/8560#discussion_r92111303
|
||||
return false;
|
||||
},
|
||||
|
||||
useSyncScheduling: true,
|
||||
});
|
||||
|
||||
ReactGenericBatching.injection.injectFiberBatchedUpdates(
|
||||
NativeRenderer.batchedUpdates
|
||||
);
|
||||
|
||||
const roots = new Map();
|
||||
|
||||
findNodeHandle.injection.injectFindNode(
|
||||
(fiber: Fiber) => {
|
||||
const instance: any = NativeRenderer.findHostInstance(fiber);
|
||||
return instance ? instance._nativeTag : null;
|
||||
}
|
||||
);
|
||||
findNodeHandle.injection.injectFindRootNodeID(
|
||||
(instance) => instance._nativeTag
|
||||
);
|
||||
|
||||
const ReactNative = {
|
||||
findNodeHandle,
|
||||
|
||||
render(element : Element<any>, containerTag : any, callback: ?Function) {
|
||||
let root = roots.get(containerTag);
|
||||
|
||||
if (!root) {
|
||||
// TODO (bvaughn): If we decide to keep the wrapper component,
|
||||
// We could create a wrapper for containerTag as well to reduce special casing.
|
||||
root = NativeRenderer.mountContainer(element, containerTag, null, callback);
|
||||
|
||||
roots.set(containerTag, root);
|
||||
} else {
|
||||
NativeRenderer.updateContainer(element, root, null, callback);
|
||||
}
|
||||
|
||||
return NativeRenderer.getPublicRootInstance(root);
|
||||
},
|
||||
|
||||
unmountComponentAtNode(containerTag : number) {
|
||||
const root = roots.get(containerTag);
|
||||
if (root) {
|
||||
// TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
|
||||
roots.delete(containerTag);
|
||||
NativeRenderer.unmountContainer(root);
|
||||
}
|
||||
},
|
||||
|
||||
unmountComponentAtNodeAndRemoveContainer(containerTag: number) {
|
||||
ReactNative.unmountComponentAtNode(containerTag);
|
||||
|
||||
// Call back into native to remove all of the subviews from this container
|
||||
UIManager.removeRootView(containerTag);
|
||||
},
|
||||
|
||||
unstable_createPortal(children: ReactNodeList, containerTag : number, key : ?string = null) {
|
||||
return ReactPortal.createPortal(children, containerTag, null, key);
|
||||
},
|
||||
|
||||
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactNative;
|
||||
@@ -15,8 +15,9 @@ var UIManager = require('UIManager');
|
||||
var ReactNativeGlobalResponderHandler = {
|
||||
onChange: function(from, to, blockNativeResponder) {
|
||||
if (to !== null) {
|
||||
// TODO (bvaughn) Clean up once Stack is deprecated
|
||||
UIManager.setJSResponder(
|
||||
to._rootNodeID,
|
||||
to._rootNodeID || to.stateNode._nativeTag,
|
||||
blockNativeResponder
|
||||
);
|
||||
} else {
|
||||
|
||||
81
src/renderers/native/ReactNativeStack.js
Normal file
81
src/renderers/native/ReactNativeStack.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
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<any>,
|
||||
mountInto: number,
|
||||
callback?: ?(() => void)
|
||||
): ?ReactComponent<any, any, any> {
|
||||
return ReactNativeMount.renderComponent(element, mountInto, callback);
|
||||
};
|
||||
|
||||
findNodeHandle.injection.injectFindNode(
|
||||
(instance) => instance.getHostNode()
|
||||
);
|
||||
findNodeHandle.injection.injectFindRootNodeID(
|
||||
(instance) => instance._rootNodeID
|
||||
);
|
||||
|
||||
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;
|
||||
@@ -30,6 +30,7 @@ var ReactNativeTextComponent = require('ReactNativeTextComponent');
|
||||
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var findNodeHandle = require('findNodeHandle');
|
||||
var invariant = require('invariant');
|
||||
|
||||
function inject() {
|
||||
@@ -61,6 +62,13 @@ function inject() {
|
||||
);
|
||||
};
|
||||
|
||||
findNodeHandle.injection.injectFindNode(
|
||||
(instance) => instance.getHostNode()
|
||||
);
|
||||
findNodeHandle.injection.injectFindRootNodeID(
|
||||
(instance) => instance._rootNodeID
|
||||
);
|
||||
|
||||
ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent);
|
||||
|
||||
ReactHostComponent.injection.injectTextComponentClass(
|
||||
|
||||
50
src/renderers/native/ReactNativeViewConfigRegistry.js
Normal file
50
src/renderers/native/ReactNativeViewConfigRegistry.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 ReactNativeViewConfigRegistry
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
export type ReactNativeBaseComponentViewConfig = {
|
||||
validAttributes: Object,
|
||||
uiViewClassName: string,
|
||||
propTypes?: Object,
|
||||
};
|
||||
|
||||
const viewConfigs = new Map();
|
||||
|
||||
const prefix = 'topsecret-';
|
||||
|
||||
const ReactNativeViewConfigRegistry = {
|
||||
register(viewConfig : ReactNativeBaseComponentViewConfig) {
|
||||
const name = viewConfig.uiViewClassName;
|
||||
invariant(
|
||||
!viewConfigs.has(name),
|
||||
'Tried to register two views with the same name %s',
|
||||
name
|
||||
);
|
||||
const secretName = prefix + name;
|
||||
viewConfigs.set(secretName, viewConfig);
|
||||
return secretName;
|
||||
},
|
||||
get(secretName: string) {
|
||||
const config = viewConfigs.get(secretName);
|
||||
invariant(
|
||||
config,
|
||||
'View config not found for name %s',
|
||||
secretName
|
||||
);
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactNativeViewConfigRegistry;
|
||||
16
src/renderers/native/__mocks__/ReactNative.js
Normal file
16
src/renderers/native/__mocks__/ReactNative.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright 2013-2015, 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
|
||||
module.exports = ReactNativeFeatureFlags.useFiber
|
||||
? require('ReactNativeFiber')
|
||||
: require('ReactNativeStack');
|
||||
@@ -12,23 +12,35 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
|
||||
const ReactNativeBaseComponent = require('ReactNativeBaseComponent');
|
||||
const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
|
||||
const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
|
||||
|
||||
// See also ReactNativeBaseComponent
|
||||
type ReactNativeBaseComponentViewConfig = {
|
||||
validAttributes: Object,
|
||||
uiViewClassName: string,
|
||||
propTypes?: Object,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} config iOS View configuration.
|
||||
* @private
|
||||
*/
|
||||
var createReactNativeComponentClass = function(
|
||||
const createReactNativeFiberComponentClass = function(
|
||||
viewConfig: ReactNativeBaseComponentViewConfig
|
||||
): string {
|
||||
return ReactNativeViewConfigRegistry.register(viewConfig);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} config iOS View configuration.
|
||||
* @private
|
||||
*/
|
||||
const createReactNativeComponentClass = function(
|
||||
viewConfig: ReactNativeBaseComponentViewConfig
|
||||
): ReactClass<any> {
|
||||
var Constructor = function(element) {
|
||||
const Constructor = function(element) {
|
||||
this._currentElement = element;
|
||||
this._topLevelWrapper = null;
|
||||
this._hostParent = null;
|
||||
@@ -45,4 +57,6 @@ var createReactNativeComponentClass = function(
|
||||
return ((Constructor: any): ReactClass<any>);
|
||||
};
|
||||
|
||||
module.exports = createReactNativeComponentClass;
|
||||
module.exports = ReactNativeFeatureFlags.useFiber
|
||||
? createReactNativeFiberComponentClass
|
||||
: createReactNativeComponentClass;
|
||||
|
||||
@@ -50,6 +50,9 @@ import type { ReactInstance } from 'ReactInstanceType';
|
||||
* nodeHandle N/A rootNodeID tag
|
||||
*/
|
||||
|
||||
let injectedFindNode;
|
||||
let injectedFindRootNodeID;
|
||||
|
||||
function findNodeHandle(componentOrHandle: any): ?number {
|
||||
if (__DEV__) {
|
||||
// TODO: fix this unsafe cast to work with Fiber.
|
||||
@@ -82,9 +85,9 @@ function findNodeHandle(componentOrHandle: any): ?number {
|
||||
// ReactInstanceMap.get here will always succeed for mounted components
|
||||
var internalInstance = ReactInstanceMap.get(component);
|
||||
if (internalInstance) {
|
||||
return internalInstance.getHostNode();
|
||||
return injectedFindNode(internalInstance);
|
||||
} else {
|
||||
var rootNodeID = component._rootNodeID;
|
||||
var rootNodeID = injectedFindRootNodeID(component);
|
||||
if (rootNodeID) {
|
||||
return rootNodeID;
|
||||
} else {
|
||||
@@ -92,7 +95,10 @@ function findNodeHandle(componentOrHandle: any): ?number {
|
||||
(
|
||||
// Native
|
||||
typeof component === 'object' &&
|
||||
'_rootNodeID' in component
|
||||
(
|
||||
'_rootNodeID' in component || // TODO (bvaughn) Clean up once Stack is deprecated
|
||||
'_nativeTag' in component
|
||||
)
|
||||
) || (
|
||||
// Composite
|
||||
component.render != null &&
|
||||
@@ -112,4 +118,14 @@ function findNodeHandle(componentOrHandle: any): ?number {
|
||||
}
|
||||
}
|
||||
|
||||
// Fiber and stack implementations differ; each must inject a strategy
|
||||
findNodeHandle.injection = {
|
||||
injectFindNode(findNode) {
|
||||
injectedFindNode = findNode;
|
||||
},
|
||||
injectFindRootNodeID(findRootNodeID) {
|
||||
injectedFindRootNodeID = findRootNodeID;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = findNodeHandle;
|
||||
|
||||
Reference in New Issue
Block a user