Merge React[DOM/Native]TreeTraversal

These two implementations are identical. Except for some
invariants for some reason.

Since this relies on an implementation detail of the internal
component tree rather than an implementation detail of the
renderer, we might as well merge them and remove the injection.
This commit is contained in:
Sebastian Markbage
2016-10-25 14:37:46 -07:00
parent e3131c1d55
commit 8df7afc689
7 changed files with 38 additions and 186 deletions
@@ -22,7 +22,6 @@ var EventPluginUtils = require('EventPluginUtils');
var HTMLDOMPropertyConfig = require('HTMLDOMPropertyConfig');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMTreeTraversal = require('ReactDOMTreeTraversal');
var ReactEventListener = require('ReactEventListener');
var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig');
var SelectEventPlugin = require('SelectEventPlugin');
@@ -48,7 +47,6 @@ function inject() {
*/
EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);
EventPluginUtils.injection.injectComponentTree(ReactDOMComponentTree);
EventPluginUtils.injection.injectTreeTraversal(ReactDOMTreeTraversal);
/**
* Some important event plugins included by default (without having to require
@@ -1,134 +0,0 @@
/**
* Copyright 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 ReactDOMTreeTraversal
*/
'use strict';
var invariant = require('invariant');
/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
*/
function getLowestCommonAncestor(instA, instB) {
invariant('_hostNode' in instA, 'getNodeFromInstance: Invalid argument.');
invariant('_hostNode' in instB, 'getNodeFromInstance: Invalid argument.');
var depthA = 0;
for (var tempA = instA; tempA; tempA = tempA._hostParent) {
depthA++;
}
var depthB = 0;
for (var tempB = instB; tempB; tempB = tempB._hostParent) {
depthB++;
}
// If A is deeper, crawl up.
while (depthA - depthB > 0) {
instA = instA._hostParent;
depthA--;
}
// If B is deeper, crawl up.
while (depthB - depthA > 0) {
instB = instB._hostParent;
depthB--;
}
// Walk in lockstep until we find a match.
var depth = depthA;
while (depth--) {
if (instA === instB) {
return instA;
}
instA = instA._hostParent;
instB = instB._hostParent;
}
return null;
}
/**
* Return if A is an ancestor of B.
*/
function isAncestor(instA, instB) {
invariant('_hostNode' in instA, 'isAncestor: Invalid argument.');
invariant('_hostNode' in instB, 'isAncestor: Invalid argument.');
while (instB) {
if (instB === instA) {
return true;
}
instB = instB._hostParent;
}
return false;
}
/**
* Return the parent instance of the passed-in instance.
*/
function getParentInstance(inst) {
invariant('_hostNode' in inst, 'getParentInstance: Invalid argument.');
return inst._hostParent;
}
/**
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
*/
function traverseTwoPhase(inst, fn, arg) {
var path = [];
while (inst) {
path.push(inst);
inst = inst._hostParent;
}
var i;
for (i = path.length; i-- > 0;) {
fn(path[i], 'captured', arg);
}
for (i = 0; i < path.length; i++) {
fn(path[i], 'bubbled', arg);
}
}
/**
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
* should would receive a `mouseEnter` or `mouseLeave` event.
*
* Does not invoke the callback on the nearest common ancestor because nothing
* "entered" or "left" that element.
*/
function traverseEnterLeave(from, to, fn, argFrom, argTo) {
var common = from && to ? getLowestCommonAncestor(from, to) : null;
var pathFrom = [];
while (from && from !== common) {
pathFrom.push(from);
from = from._hostParent;
}
var pathTo = [];
while (to && to !== common) {
pathTo.push(to);
to = to._hostParent;
}
var i;
for (i = 0; i < pathFrom.length; i++) {
fn(pathFrom[i], 'bubbled', argFrom);
}
for (i = pathTo.length; i-- > 0;) {
fn(pathTo[i], 'captured', argTo);
}
}
module.exports = {
isAncestor: isAncestor,
getLowestCommonAncestor: getLowestCommonAncestor,
getParentInstance: getParentInstance,
traverseTwoPhase: traverseTwoPhase,
traverseEnterLeave: traverseEnterLeave,
};
@@ -27,7 +27,6 @@ var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
var ReactNativeEventPluginOrder = require('ReactNativeEventPluginOrder');
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
var ReactNativeTreeTraversal = require('ReactNativeTreeTraversal');
var ResponderEventPlugin = require('ResponderEventPlugin');
function inject() {
@@ -41,7 +40,6 @@ function inject() {
*/
EventPluginHub.injection.injectEventPluginOrder(ReactNativeEventPluginOrder);
EventPluginUtils.injection.injectComponentTree(ReactNativeComponentTree);
EventPluginUtils.injection.injectTreeTraversal(ReactNativeTreeTraversal);
ResponderEventPlugin.injection.injectGlobalResponderHandler(
ReactNativeGlobalResponderHandler
@@ -6,13 +6,11 @@
* 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 ReactNativeTreeTraversal
* @providesModule ReactTreeTraversal
*/
'use strict';
// Same as ReactDOMTreeTraversal without the invariants.
/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
@@ -50,8 +50,8 @@ function renderParentIntoDocument() {
return ReactTestUtils.renderIntoDocument(<ParentComponent />);
}
describe('ReactDOMTreeTraversal', () => {
var ReactDOMTreeTraversal;
describe('ReactTreeTraversal', () => {
var ReactTreeTraversal;
var aggregatedArgs;
function argAggregator(inst, phase, arg) {
@@ -67,14 +67,14 @@ describe('ReactDOMTreeTraversal', () => {
}
beforeEach(() => {
ReactDOMTreeTraversal = require('ReactDOMTreeTraversal');
ReactTreeTraversal = require('ReactTreeTraversal');
aggregatedArgs = [];
});
describe('traverseTwoPhase', () => {
it('should not traverse when traversing outside DOM', () => {
var expectedAggregation = [];
ReactDOMTreeTraversal.traverseTwoPhase(null, argAggregator, ARG);
ReactTreeTraversal.traverseTwoPhase(null, argAggregator, ARG);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -92,7 +92,7 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1, phase: 'bubbled', arg: ARG},
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
ReactTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
@@ -103,7 +103,7 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P, phase: 'captured', arg: ARG},
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
ReactTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
expect(aggregatedArgs).toEqual(expectedAggregation);
});
});
@@ -112,7 +112,7 @@ describe('ReactDOMTreeTraversal', () => {
it('should not traverse when enter/leaving outside DOM', () => {
var target = null;
var expectedAggregation = [];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
target, target, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -123,7 +123,7 @@ describe('ReactDOMTreeTraversal', () => {
var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1);
var expectedAggregation = [];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -138,7 +138,7 @@ describe('ReactDOMTreeTraversal', () => {
// enter/leave shouldn't fire anything on the parent
{node: parent.refs.P_P1_C1.refs.DIV_2, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -151,7 +151,7 @@ describe('ReactDOMTreeTraversal', () => {
var expectedAggregation = [
{node: parent.refs.P_P1_C1.refs.DIV_1, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -166,7 +166,7 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1, phase: 'captured', arg: ARG2},
{node: parent.refs.P_P1_C1.refs.DIV, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -179,7 +179,7 @@ describe('ReactDOMTreeTraversal', () => {
var expectedAggregation = [
{node: parent.refs.P, phase: 'captured', arg: ARG2},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -194,7 +194,7 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1, phase: 'bubbled', arg: ARG},
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -209,7 +209,7 @@ describe('ReactDOMTreeTraversal', () => {
{node: parent.refs.P_P1, phase: 'bubbled', arg: ARG},
{node: parent.refs.P, phase: 'bubbled', arg: ARG},
];
ReactDOMTreeTraversal.traverseEnterLeave(
ReactTreeTraversal.traverseEnterLeave(
leave, enter, argAggregator, ARG, ARG2
);
expect(aggregatedArgs).toEqual(expectedAggregation);
@@ -261,7 +261,7 @@ describe('ReactDOMTreeTraversal', () => {
var i;
for (i = 0; i < ancestors.length; i++) {
var plan = ancestors[i];
var firstCommon = ReactDOMTreeTraversal.getLowestCommonAncestor(
var firstCommon = ReactTreeTraversal.getLowestCommonAncestor(
getInst(plan.one),
getInst(plan.two)
);
@@ -11,6 +11,7 @@
'use strict';
var ReactTreeTraversal = require('ReactTreeTraversal');
var ReactErrorUtils = require('ReactErrorUtils');
var invariant = require('invariant');
@@ -25,7 +26,6 @@ var warning = require('warning');
* and actual node references.
*/
var ComponentTree;
var TreeTraversal;
var injection = {
injectComponentTree: function(Injected) {
ComponentTree = Injected;
@@ -39,16 +39,6 @@ var injection = {
);
}
},
injectTreeTraversal: function(Injected) {
TreeTraversal = Injected;
if (__DEV__) {
warning(
Injected && Injected.isAncestor && Injected.getLowestCommonAncestor,
'EventPluginUtils.injection.injectTreeTraversal(...): Injected ' +
'module is missing isAncestor or getLowestCommonAncestor.'
);
}
},
};
function isEndish(topLevelType) {
@@ -237,19 +227,19 @@ var EventPluginUtils = {
return ComponentTree.getNodeFromInstance(node);
},
isAncestor: function(a, b) {
return TreeTraversal.isAncestor(a, b);
return ReactTreeTraversal.isAncestor(a, b);
},
getLowestCommonAncestor: function(a, b) {
return TreeTraversal.getLowestCommonAncestor(a, b);
return ReactTreeTraversal.getLowestCommonAncestor(a, b);
},
getParentInstance: function(inst) {
return TreeTraversal.getParentInstance(inst);
return ReactTreeTraversal.getParentInstance(inst);
},
traverseTwoPhase: function(target, fn, arg) {
return TreeTraversal.traverseTwoPhase(target, fn, arg);
return ReactTreeTraversal.traverseTwoPhase(target, fn, arg);
},
traverseEnterLeave: function(from, to, fn, argFrom, argTo) {
return TreeTraversal.traverseEnterLeave(from, to, fn, argFrom, argTo);
return ReactTreeTraversal.traverseEnterLeave(from, to, fn, argFrom, argTo);
},
injection: injection,
@@ -408,20 +408,9 @@ describe('ResponderEventPlugin', () => {
beforeEach(() => {
jest.resetModuleRegistry();
EventPluginHub = require('EventPluginHub');
EventPluginUtils = require('EventPluginUtils');
ResponderEventPlugin = require('ResponderEventPlugin');
EventPluginUtils.injection.injectComponentTree({
getInstanceFromNode: function(id) {
return idToInstance[id];
},
getNodeFromInstance: function(inst) {
return inst._rootNodeID;
},
});
EventPluginUtils.injection.injectTreeTraversal({
// Inline mock
const ReactTreeTraversal = require('ReactTreeTraversal');
Object.assign(ReactTreeTraversal, {
isAncestor: function(a, b) {
return isAncestorIDOf(a._rootNodeID, b._rootNodeID);
},
@@ -443,6 +432,19 @@ describe('ResponderEventPlugin', () => {
);
},
});
EventPluginHub = require('EventPluginHub');
EventPluginUtils = require('EventPluginUtils');
ResponderEventPlugin = require('ResponderEventPlugin');
EventPluginUtils.injection.injectComponentTree({
getInstanceFromNode: function(id) {
return idToInstance[id];
},
getNodeFromInstance: function(inst) {
return inst._rootNodeID;
},
});
});
it('should do nothing when no one wants to respond', () => {