@samitouri / QOS-React / commits / 475a6492c7

Utilize the backtracking during events to figure out if a Fiber was unmounted

We're walking backwards up to the root to find the parent so that we can propagate events further up to nested React parents. If we don't find a root, that means that the tree was unmounted and we shouldn't send any events to it.

Sebastian Markbage committed Nov 20, 2016 at 23:41 UTC 475a6492c78b325da97d2b7b806e9945199eb92e
3 files changed +22 -9
scripts/fiber/tests-failing.txt
-3
@@ -42,9 +42,6 @@ src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
42 * can reconcile text arbitrarily split into multiple nodes
43 * can reconcile text arbitrarily split into multiple nodes on some substitutions only
44
45 -src/renderers/dom/shared/__tests__/ReactEventIndependence-test.js
46 -* does not when event fired on unmounted tree
47 -
45 src/renderers/dom/shared/__tests__/ReactEventListener-test.js
46 * should batch between handlers from different roots
47
scripts/fiber/tests-passing.txt
+1
@@ -640,6 +640,7 @@ src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
640 src/renderers/dom/shared/__tests__/ReactEventIndependence-test.js
641 * does not crash with other react inside
642 * does not crash with other react outside
643 +* does not when event fired on unmounted tree
644
645 src/renderers/dom/shared/__tests__/ReactEventListener-test.js
646 * should dispatch events from outside React tree
src/renderers/dom/shared/ReactEventListener.js
+21 -6
@@ -16,33 +16,38 @@ var ExecutionEnvironment = require('ExecutionEnvironment');
16 var PooledClass = require('PooledClass');
17 var ReactDOMComponentTree = require('ReactDOMComponentTree');
18 var ReactGenericBatching = require('ReactGenericBatching');
19 +var ReactTypeOfWork = require('ReactTypeOfWork');
20
21 var getEventTarget = require('getEventTarget');
22 var getUnboundedScrollPosition = require('getUnboundedScrollPosition');
23
24 +var { HostContainer } = ReactTypeOfWork;
25 +
26 /**
27 * Find the deepest React component completely containing the root of the
28 * passed-in instance (for use when entire React trees are nested within each
29 * other). If React trees are not nested, returns null.
30 */
28 -function findParent(inst) {
31 +function findRootContainerNode(inst) {
32 // TODO: It may be a good idea to cache this to prevent unnecessary DOM
33 // traversal, but caching is difficult to do correctly without using a
34 // mutation observer to listen for all DOM changes.
32 - var container;
35 if (typeof inst.tag === 'number') {
36 while (inst.return) {
37 inst = inst.return;
38 }
37 - container = inst.stateNode.containerInfo;
39 + if (inst.tag !== HostContainer) {
40 + // This can happen if we're in a detached tree.
41 + return null;
42 + }
43 + return inst.stateNode.containerInfo;
44 } else {
45 while (inst._hostParent) {
46 inst = inst._hostParent;
47 }
48 var rootNode = ReactDOMComponentTree.getNodeFromInstance(inst);
43 - container = rootNode.parentNode;
49 + return rootNode.parentNode;
50 }
45 - return ReactDOMComponentTree.getClosestInstanceFromNode(container);
51 }
52
53 // Used to store ancestor hierarchy in top level callback
@@ -74,8 +79,18 @@ function handleTopLevelImpl(bookKeeping) {
79 // inconsistencies with ReactMount's node cache. See #1105.
80 var ancestor = targetInst;
81 do {
82 + if (!ancestor) {
83 + bookKeeping.ancestors.push(ancestor);
84 + break;
85 + }
86 + var root = findRootContainerNode(ancestor);
87 + if (!root) {
88 + break;
89 + }
90 bookKeeping.ancestors.push(ancestor);
78 - ancestor = ancestor && findParent(ancestor);
91 + ancestor = ReactDOMComponentTree.getClosestInstanceFromNode(
92 + root
93 + );
94 } while (ancestor);
95
96 for (var i = 0; i < bookKeeping.ancestors.length; i++) {