Remove Stack (part 4: remove Stack-only branches and types) (#10798)

* Remove findDOMNode injection and inline it

* Remove Stack-only code in ReactGenericBatching

* Remove Stack-only code branches
This commit is contained in:
Dan Abramov
2017-10-02 13:43:45 +01:00
committed by GitHub
parent df445270a6
commit 5c13e2ee58
30 changed files with 195 additions and 799 deletions
@@ -10,7 +10,6 @@
'use strict';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
/**
@@ -24,7 +23,7 @@ var ReactCurrentOwner = {
* @internal
* @type {ReactComponent}
*/
current: (null: null | ReactInstance | Fiber),
current: (null: null | Fiber),
};
module.exports = ReactCurrentOwner;
+48 -11
View File
@@ -10,7 +10,6 @@
'use strict';
import type {Fiber} from 'ReactFiber';
import type {ReactNodeList} from 'ReactTypes';
require('checkReact');
@@ -28,6 +27,7 @@ var ReactInputSelection = require('ReactInputSelection');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactPortal = require('ReactPortal');
var ReactVersion = require('ReactVersion');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var {injectInternals} = require('ReactFiberDevToolsHook');
var {
ELEMENT_NODE,
@@ -38,7 +38,7 @@ var {
} = require('HTMLNodeType');
var {ROOT_ATTRIBUTE_NAME} = require('DOMProperty');
var findDOMNode = require('findDOMNode');
var getComponentName = require('getComponentName');
var invariant = require('fbjs/lib/invariant');
var {getChildNamespace} = DOMNamespaces;
@@ -85,9 +85,6 @@ require('ReactDOMInjection');
ReactControlledComponent.injection.injectFiberControlledHostComponent(
ReactDOMFiberComponent,
);
findDOMNode._injectFiber(function(fiber: Fiber) {
return DOMRenderer.findHostInstance(fiber);
});
type DOMContainer =
| (Element & {
@@ -232,7 +229,7 @@ var DOMRenderer = ReactFiberReconciler({
if (__DEV__) {
// TODO: take namespace into account when validating.
const hostContextDev = ((hostContext: any): HostContextDev);
validateDOMNesting(type, null, null, hostContextDev.ancestorInfo);
validateDOMNesting(type, null, hostContextDev.ancestorInfo);
if (
typeof props.children === 'string' ||
typeof props.children === 'number'
@@ -243,7 +240,7 @@ var DOMRenderer = ReactFiberReconciler({
type,
null,
);
validateDOMNesting(null, string, null, ownAncestorInfo);
validateDOMNesting(null, string, ownAncestorInfo);
}
parentNamespace = hostContextDev.namespace;
} else {
@@ -298,7 +295,7 @@ var DOMRenderer = ReactFiberReconciler({
type,
null,
);
validateDOMNesting(null, string, null, ownAncestorInfo);
validateDOMNesting(null, string, ownAncestorInfo);
}
}
return diffProperties(
@@ -365,7 +362,7 @@ var DOMRenderer = ReactFiberReconciler({
): TextInstance {
if (__DEV__) {
const hostContextDev = ((hostContext: any): HostContextDev);
validateDOMNesting(null, text, null, hostContextDev.ancestorInfo);
validateDOMNesting(null, text, hostContextDev.ancestorInfo);
}
var textNode: TextInstance = createTextNode(text, rootContainerInstance);
precacheFiberNode(internalInstanceHandle, textNode);
@@ -675,6 +672,48 @@ function createPortal(
var ReactDOMFiber = {
createPortal,
findDOMNode(
componentOrElement: Element | ?React$Component<any, any>,
): null | Element | Text {
if (__DEV__) {
var owner = (ReactCurrentOwner.current: any);
if (owner !== null) {
var warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
warning(
warnedAboutRefsInRender,
'%s is accessing findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentName(owner) || 'A component',
);
owner.stateNode._warnedAboutRefsInRender = true;
}
}
if (componentOrElement == null) {
return null;
}
if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
return (componentOrElement: any);
}
var inst = ReactInstanceMap.get(componentOrElement);
if (inst) {
return DOMRenderer.findHostInstance(inst);
}
if (typeof componentOrElement.render === 'function') {
invariant(false, 'Unable to find node on an unmounted component.');
} else {
invariant(
false,
'Element appears to be neither ReactComponent nor DOMNode. Keys: %s',
Object.keys(componentOrElement),
);
}
},
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
// TODO: throw or warn if we couldn't hydrate?
return renderSubtreeIntoContainer(null, element, container, true, callback);
@@ -768,8 +807,6 @@ var ReactDOMFiber = {
}
},
findDOMNode: findDOMNode,
// Temporary alias since we already shipped React 16 RC with it.
// TODO: remove in React 17.
unstable_createPortal: createPortal,
@@ -1,16 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactDOMComponentFlags
*/
'use strict';
var ReactDOMComponentFlags = {
hasCachedChildNodes: 1 << 0,
};
module.exports = ReactDOMComponentFlags;
@@ -9,116 +9,18 @@
'use strict';
var DOMProperty = require('DOMProperty');
var ReactDOMComponentFlags = require('ReactDOMComponentFlags');
var {HostComponent, HostText} = require('ReactTypeOfWork');
var {ELEMENT_NODE, COMMENT_NODE} = require('HTMLNodeType');
var invariant = require('fbjs/lib/invariant');
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var Flags = ReactDOMComponentFlags;
var randomKey = Math.random().toString(36).slice(2);
var internalInstanceKey = '__reactInternalInstance$' + randomKey;
var internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
/**
* Check if a given node should be cached.
*/
function shouldPrecacheNode(node, nodeID) {
return (
(node.nodeType === ELEMENT_NODE &&
node.getAttribute(ATTR_NAME) === '' + nodeID) ||
(node.nodeType === COMMENT_NODE &&
node.nodeValue === ' react-text: ' + nodeID + ' ') ||
(node.nodeType === COMMENT_NODE &&
node.nodeValue === ' react-empty: ' + nodeID + ' ')
);
}
/**
* Drill down (through composites and empty components) until we get a host or
* host text component.
*
* This is pretty polymorphic but unavoidable with the current structure we have
* for `_renderedChildren`.
*/
function getRenderedHostOrTextFromComponent(component) {
var rendered;
while ((rendered = component._renderedComponent)) {
component = rendered;
}
return component;
}
/**
* Populate `_hostNode` on the rendered host/text component with the given
* DOM node. The passed `inst` can be a composite.
*/
function precacheNode(inst, node) {
var hostInst = getRenderedHostOrTextFromComponent(inst);
hostInst._hostNode = node;
node[internalInstanceKey] = hostInst;
}
function precacheFiberNode(hostInst, node) {
node[internalInstanceKey] = hostInst;
}
function uncacheNode(inst) {
var node = inst._hostNode;
if (node) {
delete node[internalInstanceKey];
inst._hostNode = null;
}
}
/**
* Populate `_hostNode` on each child of `inst`, assuming that the children
* match up with the DOM (element) children of `node`.
*
* We cache entire levels at once to avoid an n^2 problem where we access the
* children of a node sequentially and have to walk from the start to our target
* node every time.
*
* Since we update `_renderedChildren` and the actual DOM at (slightly)
* different times, we could race here and see a newer `_renderedChildren` than
* the DOM nodes we see. To avoid this, ReactMultiChild calls
* `prepareToManageChildren` before we change `_renderedChildren`, at which
* time the container's child nodes are always cached (until it unmounts).
*/
function precacheChildNodes(inst, node) {
if (inst._flags & Flags.hasCachedChildNodes) {
return;
}
var children = inst._renderedChildren;
var childNode = node.firstChild;
outer: for (var name in children) {
if (!children.hasOwnProperty(name)) {
continue;
}
var childInst = children[name];
var childID = getRenderedHostOrTextFromComponent(childInst)._domID;
if (childID === 0) {
// We're currently unmounting this child in ReactMultiChild; skip it.
continue;
}
// We assume the child nodes are in the same order as the child instances.
for (; childNode !== null; childNode = childNode.nextSibling) {
if (shouldPrecacheNode(childNode, childID)) {
precacheNode(childInst, childNode);
continue outer;
}
}
// We reached the end of the DOM children without finding an ID match.
invariant(false, 'Unable to find element with ID %s.', childID);
}
inst._flags |= Flags.hasCachedChildNodes;
}
/**
* Given a DOM node, return the closest ReactDOMComponent or
* ReactDOMTextComponent instance ancestor.
@@ -149,9 +51,6 @@ function getClosestInstanceFromNode(node) {
}
for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) {
closest = inst;
if (parents.length) {
precacheChildNodes(inst, node);
}
}
return closest;
@@ -166,18 +65,11 @@ function getInstanceFromNode(node) {
if (inst) {
if (inst.tag === HostComponent || inst.tag === HostText) {
return inst;
} else if (inst._hostNode === node) {
return inst;
} else {
return null;
}
}
inst = getClosestInstanceFromNode(node);
if (inst != null && inst._hostNode === node) {
return inst;
} else {
return null;
}
return null;
}
/**
@@ -193,33 +85,7 @@ function getNodeFromInstance(inst) {
// Without this first invariant, passing a non-DOM-component triggers the next
// invariant for a missing parent, which is super confusing.
invariant(
inst._hostNode !== undefined,
'getNodeFromInstance: Invalid argument.',
);
if (inst._hostNode) {
return inst._hostNode;
}
// Walk up the tree until we find an ancestor whose DOM node we have cached.
var parents = [];
while (!inst._hostNode) {
parents.push(inst);
invariant(
inst._hostParent,
'React DOM tree root should always have a node reference.',
);
inst = inst._hostParent;
}
// Now parents contains each ancestor that does *not* have a cached native
// node, and `inst` is the deepest ancestor that does.
for (; parents.length; inst = parents.pop()) {
precacheChildNodes(inst, inst._hostNode);
}
return inst._hostNode;
invariant(false, 'getNodeFromInstance: Invalid argument.');
}
function getFiberCurrentPropsFromNode(node) {
@@ -234,9 +100,6 @@ var ReactDOMComponentTree = {
getClosestInstanceFromNode: getClosestInstanceFromNode,
getInstanceFromNode: getInstanceFromNode,
getNodeFromInstance: getNodeFromInstance,
precacheChildNodes: precacheChildNodes,
precacheNode: precacheNode,
uncacheNode: uncacheNode,
precacheFiberNode: precacheFiberNode,
getFiberCurrentPropsFromNode,
updateFiberProps,
@@ -31,22 +31,14 @@ function findRootContainerNode(inst) {
// TODO: It may be a good idea to cache this to prevent unnecessary DOM
// traversal, but caching is difficult to do correctly without using a
// mutation observer to listen for all DOM changes.
if (typeof inst.tag === 'number') {
while (inst.return) {
inst = inst.return;
}
if (inst.tag !== HostRoot) {
// This can happen if we're in a detached tree.
return null;
}
return inst.stateNode.containerInfo;
} else {
while (inst._hostParent) {
inst = inst._hostParent;
}
var rootNode = ReactDOMComponentTree.getNodeFromInstance(inst);
return rootNode.parentNode;
while (inst.return) {
inst = inst.return;
}
if (inst.tag !== HostRoot) {
// This can happen if we're in a detached tree.
return null;
}
return inst.stateNode.containerInfo;
}
// Used to store ancestor hierarchy in top level callback
@@ -11,7 +11,6 @@
var ReactDOMFeatureFlags = {
fiberAsyncScheduling: false,
useFiber: true,
};
module.exports = ReactDOMFeatureFlags;
@@ -23,17 +23,11 @@ describe('ReactDOMComponentTree', () => {
}
function getTypeOf(instance) {
if (typeof instance.tag === 'number') {
return instance.type;
}
return instance._currentElement.type;
return instance.type;
}
function getTextOf(instance) {
if (typeof instance.tag === 'number') {
return instance.memoizedProps;
}
return instance._stringText;
return instance.memoizedProps;
}
beforeEach(() => {
@@ -34,7 +34,7 @@ import type {
DispatchConfig,
ReactSyntheticEvent,
} from 'ReactSyntheticEventType';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
import type {EventTypes, PluginModule} from 'PluginModuleType';
/**
@@ -179,7 +179,7 @@ var SimpleEventPlugin: PluginModule<MouseEvent> = {
extractEvents: function(
topLevelType: TopLevelTypes,
targetInst: ReactInstance,
targetInst: Fiber,
nativeEvent: MouseEvent,
nativeEventTarget: EventTarget,
): null | ReactSyntheticEvent {
-89
View File
@@ -1,89 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule findDOMNode
* @flow
*/
var ReactInstanceMap = require('ReactInstanceMap');
var {ELEMENT_NODE} = require('HTMLNodeType');
var {ReactCurrentOwner} = require('ReactGlobalSharedState');
var getComponentName = require('getComponentName');
var invariant = require('fbjs/lib/invariant');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
let findFiber = function(arg) {
invariant(false, 'Missing injection for fiber findDOMNode');
};
let findStack = function(arg) {
invariant(false, 'Missing injection for stack findDOMNode');
};
const findDOMNode = function(
componentOrElement: Element | ?React$Component<any, any>,
): null | Element | Text {
if (__DEV__) {
var owner = (ReactCurrentOwner.current: any);
if (owner !== null) {
var isFiber = typeof owner.tag === 'number';
var warnedAboutRefsInRender = isFiber
? owner.stateNode._warnedAboutRefsInRender
: owner._warnedAboutRefsInRender;
warning(
warnedAboutRefsInRender,
'%s is accessing findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
getComponentName(owner) || 'A component',
);
if (isFiber) {
owner.stateNode._warnedAboutRefsInRender = true;
} else {
owner._warnedAboutRefsInRender = true;
}
}
}
if (componentOrElement == null) {
return null;
}
if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
return (componentOrElement: any);
}
var inst = ReactInstanceMap.get(componentOrElement);
if (inst) {
if (typeof inst.tag === 'number') {
return findFiber(inst);
} else {
return findStack(inst);
}
}
if (typeof componentOrElement.render === 'function') {
invariant(false, 'Unable to find node on an unmounted component.');
} else {
invariant(
false,
'Element appears to be neither ReactComponent nor DOMNode. Keys: %s',
Object.keys(componentOrElement),
);
}
};
findDOMNode._injectFiber = function(fn) {
findFiber = fn;
};
findDOMNode._injectStack = function(fn) {
findStack = fn;
};
module.exports = findDOMNode;
+2 -93
View File
@@ -10,7 +10,6 @@
'use strict';
var emptyFunction = require('fbjs/lib/emptyFunction');
var getComponentName = require('getComponentName');
var validateDOMNesting = emptyFunction;
@@ -405,83 +404,9 @@ if (__DEV__) {
return null;
};
/**
* Given a ReactCompositeComponent instance, return a list of its recursive
* owners, starting at the root and ending with the instance itself.
*/
var findOwnerStack = function(instance) {
if (!instance) {
return [];
}
var stack = [];
do {
stack.push(instance);
} while ((instance = instance._currentElement._owner));
stack.reverse();
return stack;
};
var getOwnerInfo = function(
childInstance,
childTag,
ancestorInstance,
ancestorTag,
isParent,
) {
var childOwner = childInstance && childInstance._currentElement._owner;
var ancestorOwner =
ancestorInstance && ancestorInstance._currentElement._owner;
var childOwners = findOwnerStack(childOwner);
var ancestorOwners = findOwnerStack(ancestorOwner);
var minStackLen = Math.min(childOwners.length, ancestorOwners.length);
var i;
var deepestCommon = -1;
for (i = 0; i < minStackLen; i++) {
if (childOwners[i] === ancestorOwners[i]) {
deepestCommon = i;
} else {
break;
}
}
var UNKNOWN = '(unknown)';
var childOwnerNames = childOwners
.slice(deepestCommon + 1)
.map(inst => getComponentName(inst) || UNKNOWN);
var ancestorOwnerNames = ancestorOwners
.slice(deepestCommon + 1)
.map(inst => getComponentName(inst) || UNKNOWN);
var ownerInfo = []
.concat(
// If the parent and child instances have a common owner ancestor, start
// with that -- otherwise we just start with the parent's owners.
deepestCommon !== -1
? getComponentName(childOwners[deepestCommon]) || UNKNOWN
: [],
ancestorOwnerNames,
ancestorTag,
// If we're warning about an invalid (non-parent) ancestry, add '...'
isParent ? [] : ['...'],
childOwnerNames,
childTag,
)
.join(' > ');
return ownerInfo;
};
var didWarn = {};
validateDOMNesting = function(
childTag,
childText,
childInstance,
ancestorInfo,
) {
validateDOMNesting = function(childTag, childText, ancestorInfo) {
ancestorInfo = ancestorInfo || emptyAncestorInfo;
var parentInfo = ancestorInfo.current;
var parentTag = parentInfo && parentInfo.tag;
@@ -505,24 +430,8 @@ if (__DEV__) {
return;
}
var ancestorInstance = invalidParentOrAncestor.instance;
var ancestorTag = invalidParentOrAncestor.tag;
var addendum;
if (childInstance != null) {
addendum =
' See ' +
getOwnerInfo(
childInstance,
childTag,
ancestorInstance,
ancestorTag,
!!invalidParent,
) +
'.';
} else {
addendum = getCurrentFiberStackAddendum();
}
var addendum = getCurrentFiberStackAddendum();
var warnKey =
!!invalidParent + '|' + childTag + '|' + ancestorTag + '|' + addendum;
+20 -31
View File
@@ -15,7 +15,6 @@ var warnValidStyle = emptyFunction;
if (__DEV__) {
var camelizeStyleName = require('fbjs/lib/camelizeStyleName');
var getComponentName = require('getComponentName');
var warning = require('fbjs/lib/warning');
var {getCurrentFiberOwnerName} = require('ReactDebugCurrentFiber');
@@ -30,7 +29,7 @@ if (__DEV__) {
var warnedForNaNValue = false;
var warnedForInfinityValue = false;
var warnHyphenatedStyleName = function(name, owner) {
var warnHyphenatedStyleName = function(name) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@@ -41,11 +40,11 @@ if (__DEV__) {
'Unsupported style property %s. Did you mean %s?%s',
name,
camelizeStyleName(name),
checkRenderMessage(owner),
checkRenderMessage(),
);
};
var warnBadVendoredStyleName = function(name, owner) {
var warnBadVendoredStyleName = function(name) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@@ -56,11 +55,11 @@ if (__DEV__) {
'Unsupported vendor-prefixed style property %s. Did you mean %s?%s',
name,
name.charAt(0).toUpperCase() + name.slice(1),
checkRenderMessage(owner),
checkRenderMessage(),
);
};
var warnStyleValueWithSemicolon = function(name, value, owner) {
var warnStyleValueWithSemicolon = function(name, value) {
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
return;
}
@@ -70,13 +69,13 @@ if (__DEV__) {
false,
"Style property values shouldn't contain a semicolon.%s " +
'Try "%s: %s" instead.',
checkRenderMessage(owner),
checkRenderMessage(),
name,
value.replace(badStyleValueWithSemicolonPattern, ''),
);
};
var warnStyleValueIsNaN = function(name, value, owner) {
var warnStyleValueIsNaN = function(name, value) {
if (warnedForNaNValue) {
return;
}
@@ -86,11 +85,11 @@ if (__DEV__) {
false,
'`NaN` is an invalid value for the `%s` css style property.%s',
name,
checkRenderMessage(owner),
checkRenderMessage(),
);
};
var warnStyleValueIsInfinity = function(name, value, owner) {
var warnStyleValueIsInfinity = function(name, value) {
if (warnedForInfinityValue) {
return;
}
@@ -100,21 +99,16 @@ if (__DEV__) {
false,
'`Infinity` is an invalid value for the `%s` css style property.%s',
name,
checkRenderMessage(owner),
checkRenderMessage(),
);
};
var checkRenderMessage = function(owner) {
var checkRenderMessage = function() {
var ownerName;
if (owner != null) {
// Stack passes the owner manually all the way to CSSPropertyOperations.
ownerName = getComponentName(owner);
} else {
// Fiber doesn't pass it but uses ReactDebugCurrentFiber to track it.
// It is only enabled in development and tracks host components too.
ownerName = getCurrentFiberOwnerName();
// TODO: also report the stack.
}
// Fiber doesn't pass it but uses ReactDebugCurrentFiber to track it.
// It is only enabled in development and tracks host components too.
ownerName = getCurrentFiberOwnerName();
// TODO: also report the stack.
if (ownerName) {
return '\n\nCheck the render method of `' + ownerName + '`.';
}
@@ -122,24 +116,19 @@ if (__DEV__) {
};
warnValidStyle = function(name, value, component) {
var owner;
if (component) {
// TODO: this only works with Stack. Seems like we need to add unit tests?
owner = component._currentElement._owner;
}
if (name.indexOf('-') > -1) {
warnHyphenatedStyleName(name, owner);
warnHyphenatedStyleName(name);
} else if (badVendoredStyleNamePattern.test(name)) {
warnBadVendoredStyleName(name, owner);
warnBadVendoredStyleName(name);
} else if (badStyleValueWithSemicolonPattern.test(value)) {
warnStyleValueWithSemicolon(name, value, owner);
warnStyleValueWithSemicolon(name, value);
}
if (typeof value === 'number') {
if (isNaN(value)) {
warnStyleValueIsNaN(name, value, owner);
warnStyleValueIsNaN(name, value);
} else if (!isFinite(value)) {
warnStyleValueIsInfinity(name, value, owner);
warnStyleValueIsInfinity(name, value);
}
}
};
+2 -38
View File
@@ -43,35 +43,6 @@ function Event(suffix) {}
* @class ReactTestUtils
*/
function findAllInRenderedStackTreeInternal(inst, test) {
if (!inst || !inst.getPublicInstance) {
return [];
}
var publicInst = inst.getPublicInstance();
var ret = test(publicInst) ? [publicInst] : [];
var currentElement = inst._currentElement;
if (ReactTestUtils.isDOMComponent(publicInst)) {
var renderedChildren = inst._renderedChildren;
var key;
for (key in renderedChildren) {
if (!renderedChildren.hasOwnProperty(key)) {
continue;
}
ret = ret.concat(
findAllInRenderedStackTreeInternal(renderedChildren[key], test),
);
}
} else if (
React.isValidElement(currentElement) &&
typeof currentElement.type === 'function'
) {
ret = ret.concat(
findAllInRenderedStackTreeInternal(inst._renderedComponent, test),
);
}
return ret;
}
function findAllInRenderedFiberTreeInternal(fiber, test) {
if (!fiber) {
return [];
@@ -169,10 +140,7 @@ var ReactTestUtils = {
return false;
}
var internalInstance = ReactInstanceMap.get(inst);
var constructor = typeof internalInstance.tag === 'number'
? internalInstance.type // Fiber reconciler
: internalInstance._currentElement.type; // Stack reconciler
var constructor = internalInstance.type;
return constructor === type;
},
@@ -185,11 +153,7 @@ var ReactTestUtils = {
'findAllInRenderedTree(...): instance must be a composite component',
);
var internalInstance = ReactInstanceMap.get(inst);
if (internalInstance && typeof internalInstance.tag === 'number') {
return findAllInRenderedFiberTreeInternal(internalInstance, test);
} else {
return findAllInRenderedStackTreeInternal(internalInstance, test);
}
return findAllInRenderedFiberTreeInternal(internalInstance, test);
},
/**
@@ -14,41 +14,10 @@ var invariant = require('fbjs/lib/invariant');
var instanceCache = {};
var instanceProps = {};
/**
* Drill down (through composites and empty components) until we get a host or
* host text component.
*
* This is pretty polymorphic but unavoidable with the current structure we have
* for `_renderedChildren`.
*/
function getRenderedHostOrTextFromComponent(component) {
var rendered;
while ((rendered = component._renderedComponent)) {
component = rendered;
}
return component;
}
/**
* Populate `_hostNode` on the rendered host/text component with the given
* DOM node. The passed `inst` can be a composite.
*/
function precacheNode(inst, tag) {
var nativeInst = getRenderedHostOrTextFromComponent(inst);
instanceCache[tag] = nativeInst;
}
function precacheFiberNode(hostInst, tag) {
instanceCache[tag] = hostInst;
}
function uncacheNode(inst) {
var tag = inst._rootNodeID;
if (tag) {
delete instanceCache[tag];
}
}
function uncacheFiberNode(tag) {
delete instanceCache[tag];
delete instanceProps[tag];
@@ -77,9 +46,7 @@ var ReactNativeComponentTree = {
getInstanceFromNode: getInstanceFromTag,
getNodeFromInstance: getTagFromInstance,
precacheFiberNode,
precacheNode,
uncacheFiberNode,
uncacheNode,
getFiberCurrentPropsFromNode,
updateFiberProps,
};
@@ -250,9 +250,7 @@ it('handles when a responder is unmounted while a touch sequence is in progress'
if (responder === null) {
return null;
}
const props = typeof responder.tag === 'number'
? responder.memoizedProps
: responder._currentElement.props;
const props = responder.memoizedProps;
return props ? props.id : null;
}
@@ -342,9 +340,7 @@ it('handles events without target', () => {
if (responder === null) {
return null;
}
const props = typeof responder.tag === 'number'
? responder.memoizedProps
: responder._currentElement.props;
const props = responder.memoizedProps;
return props ? props.id : null;
}
+7 -13
View File
@@ -13,7 +13,6 @@
import type {ReactElement} from 'ReactElementType';
import type {ReactCoroutine, ReactPortal, ReactYield} from 'ReactTypes';
import type {Fiber} from 'ReactFiber';
import type {ReactInstance} from 'ReactInstanceType';
import type {PriorityLevel} from 'ReactPriorityLevel';
var {REACT_COROUTINE_TYPE, REACT_YIELD_TYPE} = require('ReactCoroutine');
@@ -120,20 +119,15 @@ function coerceRef(current: Fiber | null, element: ReactElement) {
let mixedRef = element.ref;
if (mixedRef !== null && typeof mixedRef !== 'function') {
if (element._owner) {
const owner: ?(Fiber | ReactInstance) = (element._owner: any);
const owner: ?Fiber = (element._owner: any);
let inst;
if (owner) {
if (typeof owner.tag === 'number') {
const ownerFiber = ((owner: any): Fiber);
invariant(
ownerFiber.tag === ClassComponent,
'Stateless function components cannot have refs.',
);
inst = ownerFiber.stateNode;
} else {
// Stack
inst = (owner: any).getPublicInstance();
}
const ownerFiber = ((owner: any): Fiber);
invariant(
ownerFiber.tag === ClassComponent,
'Stateless function components cannot have refs.',
);
inst = ownerFiber.stateNode;
}
invariant(
inst,
@@ -29,8 +29,9 @@ function getCurrentFiberOwnerName(): string | null {
if (fiber === null) {
return null;
}
if (fiber._debugOwner != null) {
return getComponentName(fiber._debugOwner);
const owner = fiber._debugOwner;
if (owner !== null && typeof owner !== 'undefined') {
return getComponentName(owner);
}
}
return null;
+3 -4
View File
@@ -11,7 +11,6 @@
'use strict';
import type {ReactElement, Source} from 'ReactElementType';
import type {ReactInstance, DebugID} from 'ReactInstanceType';
import type {
ReactCoroutine,
ReactFragment,
@@ -147,9 +146,9 @@ export type Fiber = {|
// workInProgress : Fiber -> alternate The alternate used for reuse happens
// to be the same as work in progress.
// __DEV__ only
_debugID?: DebugID,
_debugID?: number,
_debugSource?: Source | null,
_debugOwner?: Fiber | ReactInstance | null, // Stack compatible
_debugOwner?: Fiber | null,
_debugIsCurrentlyTiming?: boolean,
|};
@@ -349,7 +348,7 @@ function createFiberFromElementType(
type: mixed,
key: null | string,
internalContextTag: TypeOfInternalContext,
debugOwner: null | Fiber | ReactInstance,
debugOwner: null | Fiber,
): Fiber {
let fiber;
if (typeof type === 'function') {
@@ -25,7 +25,9 @@ var {
} = require('ReactFiberContext');
var {createFiberRoot} = require('ReactFiberRoot');
var ReactFiberScheduler = require('ReactFiberScheduler');
var ReactInstanceMap = require('ReactInstanceMap');
var {HostComponent} = require('ReactTypeOfWork');
var emptyObject = require('fbjs/lib/emptyObject');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
@@ -39,8 +41,6 @@ var {
findCurrentHostFiberWithNoPortals,
} = require('ReactFiberTreeReflection');
var getContextForSubtree = require('getContextForSubtree');
export type Deadline = {
timeRemaining: () => number,
};
@@ -178,12 +178,19 @@ export type Reconciler<C, I, TI> = {
findHostInstanceWithNoPortals(component: Fiber): I | TI | null,
};
getContextForSubtree._injectFiber(function(fiber: Fiber) {
function getContextForSubtree(
parentComponent: ?React$Component<any, any>,
): Object {
if (!parentComponent) {
return emptyObject;
}
const fiber = ReactInstanceMap.get(parentComponent);
const parentContext = findCurrentUnmaskedContext(fiber);
return isContextProvider(fiber)
? processChildContext(fiber, parentContext)
: parentContext;
});
}
module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
@@ -1,52 +0,0 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactHostOperationHistoryHook
* @flow
*/
'use strict';
import type {DebugID} from 'ReactInstanceType';
export type Operation = {instanceID: DebugID} & (
| {type: 'mount', payload: string}
| {type: 'insert child', payload: {toIndex: number, content: string}}
| {type: 'move child', payload: {fromIndex: number, toIndex: number}}
| {type: 'replace children', payload: string}
| {type: 'replace text', payload: string}
| {type: 'replace with', payload: string}
| {type: 'update styles', payload: mixed /* Style Object */}
| {type: 'update attribute', payload: {[name: string]: string}}
| {type: 'remove attribute', payload: string});
// Trust the developer to only use this with a __DEV__ check
var ReactHostOperationHistoryHook = ((null: any): typeof ReactHostOperationHistoryHook);
if (__DEV__) {
var history: Array<Operation> = [];
ReactHostOperationHistoryHook = {
onHostOperation(operation: Operation) {
history.push(operation);
},
clearHistory(): void {
if (ReactHostOperationHistoryHook._preventClearing) {
// Should only be used for tests.
return;
}
history = [];
},
getHistory(): Array<Operation> {
return history;
},
};
}
module.exports = ReactHostOperationHistoryHook;
@@ -1,39 +0,0 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactInvalidSetStateWarningHook
* @flow
*/
'use strict';
var ReactInvalidSetStateWarningHook = {};
if (__DEV__) {
var warning = require('fbjs/lib/warning');
var processingChildContext = false;
var warnInvalidSetState = function() {
warning(
!processingChildContext,
'setState(...): Cannot call setState() inside getChildContext()',
);
};
ReactInvalidSetStateWarningHook = {
onBeginProcessingChildContext(): void {
processingChildContext = true;
},
onEndProcessingChildContext(): void {
processingChildContext = false;
},
onSetState(): void {
warnInvalidSetState();
},
};
}
module.exports = ReactInvalidSetStateWarningHook;
@@ -12,21 +12,16 @@
var {HostComponent} = require('ReactTypeOfWork');
function getParent(inst) {
if (inst._hostParent !== undefined) {
return inst._hostParent;
}
if (typeof inst.tag === 'number') {
do {
inst = inst.return;
// TODO: If this is a HostRoot we might want to bail out.
// That is depending on if we want nested subtrees (layers) to bubble
// events to their parent. We could also go through parentNode on the
// host node but that wouldn't work for React Native and doesn't let us
// do the portal feature.
} while (inst && inst.tag !== HostComponent);
if (inst) {
return inst;
}
do {
inst = inst.return;
// TODO: If this is a HostRoot we might want to bail out.
// That is depending on if we want nested subtrees (layers) to bubble
// events to their parent. We could also go through parentNode on the
// host node but that wouldn't work for React Native and doesn't let us
// do the portal feature.
} while (inst && inst.tag !== HostComponent);
if (inst) {
return inst;
}
return null;
}
@@ -122,43 +122,20 @@ var EventPluginHub = {
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
// live here; needs to be moved to a better place soon
if (typeof inst.tag === 'number') {
const stateNode = inst.stateNode;
if (!stateNode) {
// Work in progress (ex: onload events in incremental mode).
return null;
}
const props = EventPluginUtils.getFiberCurrentPropsFromNode(stateNode);
if (!props) {
// Work in progress.
return null;
}
listener = props[registrationName];
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
return null;
}
} else {
const currentElement = inst._currentElement;
if (
typeof currentElement === 'string' ||
typeof currentElement === 'number'
) {
// Text node, let it bubble through.
return null;
}
if (!inst._rootNodeID) {
// If the instance is already unmounted, we have no listeners.
return null;
}
const props = currentElement.props;
listener = props[registrationName];
if (
shouldPreventMouseEvent(registrationName, currentElement.type, props)
) {
return null;
}
const stateNode = inst.stateNode;
if (!stateNode) {
// Work in progress (ex: onload events in incremental mode).
return null;
}
const props = EventPluginUtils.getFiberCurrentPropsFromNode(stateNode);
if (!props) {
// Work in progress.
return null;
}
listener = props[registrationName];
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
return null;
}
invariant(
!listener || typeof listener === 'function',
'Expected `%s` listener to be a function, instead got a value of `%s` type.',
@@ -10,7 +10,7 @@
'use strict';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
import type {
DispatchConfig,
ReactSyntheticEvent,
@@ -26,7 +26,7 @@ export type PluginModule<NativeEvent> = {
eventTypes: EventTypes,
extractEvents: (
topLevelType: string,
targetInst: ReactInstance,
targetInst: Fiber,
nativeTarget: NativeEvent,
nativeEventTarget: EventTarget,
) => null | ReactSyntheticEvent,
@@ -36,30 +36,20 @@ function restoreStateOfTarget(target) {
// Unmounted
return;
}
if (typeof internalInstance.tag === 'number') {
invariant(
fiberHostComponent &&
typeof fiberHostComponent.restoreControlledState === 'function',
'Fiber needs to be injected to handle a fiber target for controlled ' +
'events. This error is likely caused by a bug in React. Please file an issue.',
);
const props = EventPluginUtils.getFiberCurrentPropsFromNode(
internalInstance.stateNode,
);
fiberHostComponent.restoreControlledState(
internalInstance.stateNode,
internalInstance.type,
props,
);
return;
}
invariant(
typeof internalInstance.restoreControlledState === 'function',
'The internal instance must be a React host component. ' +
'This error is likely caused by a bug in React. Please file an issue.',
fiberHostComponent &&
typeof fiberHostComponent.restoreControlledState === 'function',
'Fiber needs to be injected to handle a fiber target for controlled ' +
'events. This error is likely caused by a bug in React. Please file an issue.',
);
const props = EventPluginUtils.getFiberCurrentPropsFromNode(
internalInstance.stateNode,
);
fiberHostComponent.restoreControlledState(
internalInstance.stateNode,
internalInstance.type,
props,
);
// If it is not a Fiber, we can just use dynamic dispatch.
internalInstance.restoreControlledState();
}
var ReactControlledComponent = {
@@ -11,30 +11,22 @@
var ReactControlledComponent = require('ReactControlledComponent');
// Used as a way to call batchedUpdates when we don't know if we're in a Fiber
// or Stack context. Such as when we're dispatching events or if third party
// Used as a way to call batchedUpdates when we don't have a reference to
// the renderer. Such as when we're dispatching events or if third party
// libraries need to call batchedUpdates. Eventually, this API will go away when
// everything is batched by default. We'll then have a similar API to opt-out of
// scheduled work and instead do synchronous work.
// Defaults
var stackBatchedUpdates = function(fn, a, b, c, d, e) {
return fn(a, b, c, d, e);
};
var fiberBatchedUpdates = function(fn, bookkeeping) {
return fn(bookkeeping);
};
function performFiberBatchedUpdates(fn, bookkeeping) {
function batchedUpdates(fn, bookkeeping) {
// If we have Fiber loaded, we need to wrap this in a batching call so that
// Fiber can apply its default priority for this call.
return fiberBatchedUpdates(fn, bookkeeping);
}
function batchedUpdates(fn, bookkeeping) {
// We first perform work with the stack batching strategy, by passing our
// indirection to it.
return stackBatchedUpdates(performFiberBatchedUpdates, fn, bookkeeping);
}
var isNestingBatched = false;
function batchedUpdatesWithControlledComponents(fn, bookkeeping) {
@@ -58,9 +50,6 @@ function batchedUpdatesWithControlledComponents(fn, bookkeeping) {
}
var ReactGenericBatchingInjection = {
injectStackBatchedUpdates: function(_batchedUpdates) {
stackBatchedUpdates = _batchedUpdates;
},
injectFiberBatchedUpdates: function(_batchedUpdates) {
fiberBatchedUpdates = _batchedUpdates;
},
@@ -12,7 +12,7 @@
'use strict';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
export type DispatchConfig = {
dependencies: Array<string>,
@@ -27,7 +27,7 @@ export type ReactSyntheticEvent = {
dispatchConfig: DispatchConfig,
getPooled: (
dispatchConfig: DispatchConfig,
targetInst: ReactInstance,
targetInst: Fiber,
nativeTarget: Event,
nativeEventTarget: EventTarget,
) => ReactSyntheticEvent,
@@ -9,6 +9,8 @@
'use strict';
var {HostComponent} = require('ReactTypeOfWork');
var EventPluginHub;
var ResponderEventPlugin;
@@ -318,35 +320,37 @@ var PARENT_HOST_NODE = {};
var CHILD_HOST_NODE = {};
var CHILD_HOST_NODE2 = {};
// These intentionally look like Fibers. ReactTreeTraversal depends on their field names.
// TODO: we could test this with regular DOM nodes (and real fibers) instead.
var GRANDPARENT_INST = {
_hostParent: null,
_rootNodeID: '1',
_hostNode: GRANDPARENT_HOST_NODE,
_currentElement: {props: {}},
return: null,
tag: HostComponent,
stateNode: GRANDPARENT_HOST_NODE,
memoizedProps: {},
};
var PARENT_INST = {
_hostParent: GRANDPARENT_INST,
_rootNodeID: '2',
_hostNode: PARENT_HOST_NODE,
_currentElement: {props: {}},
return: GRANDPARENT_INST,
tag: HostComponent,
stateNode: PARENT_HOST_NODE,
memoizedProps: {},
};
var CHILD_INST = {
_hostParent: PARENT_INST,
_rootNodeID: '3',
_hostNode: CHILD_HOST_NODE,
_currentElement: {props: {}},
return: PARENT_INST,
tag: HostComponent,
stateNode: CHILD_HOST_NODE,
memoizedProps: {},
};
var CHILD_INST2 = {
_hostParent: PARENT_INST,
_rootNodeID: '4',
_hostNode: CHILD_HOST_NODE2,
_currentElement: {props: {}},
return: PARENT_INST,
tag: HostComponent,
stateNode: CHILD_HOST_NODE2,
memoizedProps: {},
};
GRANDPARENT_HOST_NODE._reactInstance = GRANDPARENT_INST;
PARENT_HOST_NODE._reactInstance = PARENT_INST;
CHILD_HOST_NODE._reactInstance = CHILD_INST;
CHILD_HOST_NODE2._reactInstance = CHILD_INST2;
GRANDPARENT_HOST_NODE.testInstance = GRANDPARENT_INST;
PARENT_HOST_NODE.testInstance = PARENT_INST;
CHILD_HOST_NODE.testInstance = CHILD_INST;
CHILD_HOST_NODE2.testInstance = CHILD_INST2;
var three = {
grandParent: GRANDPARENT_HOST_NODE,
@@ -361,19 +365,23 @@ var siblings = {
};
function getInstanceFromNode(node) {
return node._reactInstance;
return node.testInstance;
}
function getNodeFromInstance(inst) {
return inst._hostNode;
return inst.stateNode;
}
function putListener(node, registrationName, handler) {
node._currentElement.props[registrationName] = handler;
function getFiberCurrentPropsFromNode(node) {
return node.testInstance.memoizedProps;
}
function deleteAllListeners(node) {
node._currentElement.props = {};
function putListener(instance, registrationName, handler) {
instance.memoizedProps[registrationName] = handler;
}
function deleteAllListeners(instance) {
instance.memoizedProps = {};
}
describe('ResponderEventPlugin', () => {
@@ -398,6 +406,7 @@ describe('ResponderEventPlugin', () => {
injectComponentTree({
getInstanceFromNode,
getNodeFromInstance,
getFiberCurrentPropsFromNode,
});
});
@@ -1,39 +0,0 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule getContextForSubtree
* @flow
*/
const ReactInstanceMap = require('ReactInstanceMap');
const emptyObject = require('fbjs/lib/emptyObject');
const invariant = require('fbjs/lib/invariant');
let getContextFiber = function(arg) {
invariant(false, 'Missing injection for fiber getContextForSubtree');
};
function getContextForSubtree(
parentComponent: ?React$Component<any, any>,
): Object {
if (!parentComponent) {
return emptyObject;
}
const instance = ReactInstanceMap.get(parentComponent);
if (typeof instance.tag === 'number') {
return getContextFiber(instance);
} else {
return instance._processChildContext(instance._context);
}
}
getContextForSubtree._injectFiber = function(fn) {
getContextFiber = fn;
};
module.exports = getContextForSubtree;
-27
View File
@@ -1,27 +0,0 @@
/**
* Copyright (c) 2016-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ReactInstanceType
* @flow
*/
'use strict';
// TODO: delete this file.
// It was only necessary for Stack.
import type {ReactElement} from 'ReactElementType';
export type DebugID = number;
export type ReactInstance = {
mountComponent: any,
unmountComponent: any,
receiveComponent: any,
getName: () => string,
getPublicInstance: any,
_currentElement: ReactElement,
};
+6 -18
View File
@@ -10,27 +10,15 @@
'use strict';
import type {ReactInstance} from 'ReactInstanceType';
import type {Fiber} from 'ReactFiber';
function getComponentName(
instanceOrFiber: ReactInstance | Fiber,
): string | null {
if (typeof instanceOrFiber.getName === 'function') {
// Stack reconciler
const instance = ((instanceOrFiber: any): ReactInstance);
return instance.getName();
function getComponentName(fiber: Fiber): string | null {
const {type} = fiber;
if (typeof type === 'string') {
return type;
}
if (typeof instanceOrFiber.tag === 'number') {
// Fiber reconciler
const fiber = ((instanceOrFiber: any): Fiber);
const {type} = fiber;
if (typeof type === 'string') {
return type;
}
if (typeof type === 'function') {
return type.displayName || type.name;
}
if (typeof type === 'function') {
return type.displayName || type.name;
}
return null;
}