Just ignore events on unmounted components
Fixes #4865 and also seems to fixes #3790.
Ben Alpert committed
Sep 17, 2015 at 17:27 UTC
45f85a6cc5707cd798aba350ae4f6dbe99ede6dc
2 files changed
+75
-6
src/renderers/dom/client/ReactMount.js
+5
-6
@@ -392,12 +392,11 @@ function findFirstReactDOMImpl(node) {
392
do {
393
lastID = internalGetID(current);
394
current = current.parentNode;
395
- invariant(
396
- current != null,
397
- 'findFirstReactDOMImpl(...): Unexpected detached subtree found when ' +
398
- 'traversing DOM from node `%s`.',
399
- nodeID
400
- );
395
+ if (current == null) {
396
+ // The passed-in node has been detached from the container it was
397
+ // originally rendered into.
398
+ return null;
399
+ }
400
} while (lastID !== reactRootID);
401
402
if (current === containersByReactRootID[reactRootID]) {
src/renderers/dom/client/__tests__/ReactEventIndependence-test.js
new
+70
@@ -0,0 +1,70 @@
1
+/**
2
+ * Copyright 2013-2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @emails react-core
10
+ */
11
+
12
+'use strict';
13
+
14
+var React;
15
+var ReactDOM;
16
+var ReactTestUtils;
17
+
18
+describe('ReactEventIndependence', function() {
19
+ beforeEach(function() {
20
+ require('mock-modules').dumpCache();
21
+
22
+ React = require('React');
23
+ ReactDOM = require('ReactDOM');
24
+ ReactTestUtils = require('ReactTestUtils');
25
+ });
26
+
27
+ it('does not crash with other react inside', function() {
28
+ var clicks = 0;
29
+ var div = ReactTestUtils.renderIntoDocument(
30
+ <div
31
+ onClick={() => clicks++}
32
+ dangerouslySetInnerHTML={{
33
+ __html: '<button data-reactid=".z">click me</div>',
34
+ }}
35
+ />
36
+ );
37
+ ReactTestUtils.SimulateNative.click(div.firstChild);
38
+ expect(clicks).toBe(1);
39
+ });
40
+
41
+ it('does not crash with other react outside', function() {
42
+ var clicks = 0;
43
+ var outer = document.createElement('div');
44
+ outer.setAttribute('data-reactid', '.z');
45
+ var inner = ReactDOM.render(
46
+ <button onClick={() => clicks++}>click me</button>,
47
+ outer
48
+ );
49
+ ReactTestUtils.SimulateNative.click(inner);
50
+ expect(clicks).toBe(1);
51
+ });
52
+
53
+ it('does not when event fired on unmounted tree', function() {
54
+ var clicks = 0;
55
+ var container = document.createElement('div');
56
+ var button = ReactDOM.render(
57
+ <button onClick={() => clicks++}>click me</button>,
58
+ container
59
+ );
60
+
61
+ // Now we unmount the component, as if caused by a non-React event handler
62
+ // for the same click we're about to simulate, like closing a layer:
63
+ ReactDOM.unmountComponentAtNode(container);
64
+ ReactTestUtils.SimulateNative.click(button);
65
+
66
+ // Since the tree is unmounted, we don't dispatch the click event.
67
+ expect(clicks).toBe(0);
68
+ });
69
+
70
+});