Make findDOMNode generic (#8348)
Ben Alpert committed
Nov 21, 2016 at 17:13 UTC
3438d503db2aba2b0e036912fa708cd3322310df
9 files changed
+84
-65
scripts/fiber/tests-failing.txt
-4
@@ -108,10 +108,6 @@ src/renderers/dom/stack/client/__tests__/ReactRenderDocument-test.js
108
* should throw on full document render w/ no markup
109
* supports findDOMNode on full-page components
110
111
-src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
112
-* findDOMNode should reject random objects
113
-* findDOMNode should reject unmounted objects with render func
114
-
111
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
112
* should generate simple markup
113
* should generate simple markup for self-closing tags
scripts/fiber/tests-passing.txt
+2
@@ -908,6 +908,8 @@ src/renderers/dom/stack/client/__tests__/ReactMountDestruction-test.js
908
src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
909
* findDOMNode should return null if passed null
910
* findDOMNode should find dom element
911
+* findDOMNode should reject random objects
912
+* findDOMNode should reject unmounted objects with render func
913
* findDOMNode should not throw an error when called within a component that is not mounted
914
915
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
src/renderers/dom/fiber/ReactDOMFiber.js
+11
-16
@@ -12,6 +12,7 @@
12
13
'use strict';
14
15
+import type { Fiber } from 'ReactFiber';
16
import type { HostChildren } from 'ReactFiberReconciler';
17
18
var ReactControlledComponent = require('ReactControlledComponent');
@@ -21,11 +22,7 @@ var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
22
var ReactDOMFiberComponent = require('ReactDOMFiberComponent');
23
var ReactDOMInjection = require('ReactDOMInjection');
24
24
-ReactDOMInjection.inject();
25
-ReactControlledComponent.injection.injectFiberControlledHostComponent(
26
- ReactDOMFiberComponent
27
-);
28
-
25
+var findDOMNode = require('findDOMNode');
26
var warning = require('warning');
27
28
var {
@@ -35,6 +32,14 @@ var {
32
} = ReactDOMFiberComponent;
33
var { precacheFiberNode } = ReactDOMComponentTree;
34
35
+ReactDOMInjection.inject();
36
+ReactControlledComponent.injection.injectFiberControlledHostComponent(
37
+ ReactDOMFiberComponent
38
+);
39
+findDOMNode._injectFiber(function(fiber: Fiber) {
40
+ return DOMRenderer.findHostInstance(fiber);
41
+});
42
+
43
type DOMContainerElement = Element & { _reactRootContainer: ?Object };
44
45
type Container = Element;
@@ -167,17 +172,7 @@ var ReactDOM = {
172
}
173
},
174
170
- findDOMNode(componentOrElement : Element | ?ReactComponent<any, any, any>) : null | Element | Text {
171
- if (componentOrElement == null) {
172
- return null;
173
- }
174
- // Unsound duck typing.
175
- const component = (componentOrElement : any);
176
- if (component.nodeType === 1) {
177
- return component;
178
- }
179
- return DOMRenderer.findHostInstance(component);
180
- },
175
+ findDOMNode: findDOMNode,
176
177
unstable_batchedUpdates<A>(fn : () => A) : A {
178
return DOMRenderer.batchedUpdates(fn);
src/renderers/dom/shared/findDOMNode.js
renamed
+30
-23
@@ -7,59 +7,59 @@
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule findDOMNode
10
+ * @flow
11
*/
12
12
-'use strict';
13
-
13
var ReactCurrentOwner = require('ReactCurrentOwner');
15
-var ReactDOMComponentTree = require('ReactDOMComponentTree');
14
var ReactInstanceMap = require('ReactInstanceMap');
15
18
-var getHostComponentFromComposite = require('getHostComponentFromComposite');
16
+var getComponentName = require('getComponentName');
17
var invariant = require('invariant');
18
var warning = require('warning');
19
22
-/**
23
- * Returns the DOM node rendered by this element.
24
- *
25
- * See https://facebook.github.io/react/docs/react-dom.html#finddomnode
26
- *
27
- * @param {ReactComponent|DOMElement} componentOrElement
28
- * @return {?DOMElement} The root node of this element.
29
- */
30
-function findDOMNode(componentOrElement) {
20
+let findFiber = function(arg) {
21
+ invariant(false, 'Missing injection for fiber findDOMNode');
22
+};
23
+let findStack = function(arg) {
24
+ invariant(false, 'Missing injection for stack findDOMNode');
25
+};
26
+
27
+const findDOMNode = function(componentOrElement : Element | ?ReactComponent<any, any, any>) : null | Element | Text {
28
if (__DEV__) {
29
var owner = ReactCurrentOwner.current;
33
- if (owner !== null) {
30
+ if (owner !== null && '_warnedAboutRefsInRender' in owner) {
31
warning(
35
- owner._warnedAboutRefsInRender,
32
+ (owner: any)._warnedAboutRefsInRender,
33
'%s is accessing findDOMNode inside its render(). ' +
34
'render() should be a pure function of props and state. It should ' +
35
'never access something that requires stale data from the previous ' +
36
'render, such as refs. Move this logic to componentDidMount and ' +
37
'componentDidUpdate instead.',
41
- owner.getName() || 'A component'
38
+ getComponentName(owner) || 'A component'
39
);
43
- owner._warnedAboutRefsInRender = true;
40
+ (owner: any)._warnedAboutRefsInRender = true;
41
}
42
}
43
if (componentOrElement == null) {
44
return null;
45
}
49
- if (componentOrElement.nodeType === 1) {
50
- return componentOrElement;
46
+ if ((componentOrElement: any).nodeType === 1) {
47
+ return (componentOrElement: any);
48
}
49
50
var inst = ReactInstanceMap.get(componentOrElement);
51
if (inst) {
55
- inst = getHostComponentFromComposite(inst);
56
- return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
52
+ if (typeof inst.tag === 'number') {
53
+ return findFiber(inst);
54
+ } else {
55
+ return findStack(inst);
56
+ }
57
}
58
59
if (typeof componentOrElement.render === 'function') {
60
invariant(
61
false,
62
- 'findDOMNode was called on an unmounted component.'
62
+ 'Unable to find node on an unmounted component.'
63
);
64
} else {
65
invariant(
@@ -68,6 +68,13 @@ function findDOMNode(componentOrElement) {
68
Object.keys(componentOrElement)
69
);
70
}
71
-}
71
+};
72
+
73
+findDOMNode._injectFiber = function(fn) {
74
+ findFiber = fn;
75
+};
76
+findDOMNode._injectStack = function(fn) {
77
+ findStack = fn;
78
+};
79
80
module.exports = findDOMNode;
src/renderers/dom/stack/client/ReactDOMStackInjection.js
+9
@@ -15,6 +15,7 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment');
15
var ReactComponentBrowserEnvironment =
16
require('ReactComponentBrowserEnvironment');
17
var ReactDOMComponent = require('ReactDOMComponent');
18
+var ReactDOMComponentTree = require('ReactDOMComponentTree');
19
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
20
var ReactDOMTextComponent = require('ReactDOMTextComponent');
21
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
@@ -24,6 +25,9 @@ var ReactHostComponent = require('ReactHostComponent');
25
var ReactReconcileTransaction = require('ReactReconcileTransaction');
26
var ReactUpdates = require('ReactUpdates');
27
28
+var findDOMNode = require('findDOMNode');
29
+var getHostComponentFromComposite = require('getHostComponentFromComposite');
30
+
31
var alreadyInjected = false;
32
33
function inject() {
@@ -61,6 +65,11 @@ function inject() {
65
);
66
67
ReactComponentEnvironment.injection.injectEnvironment(ReactComponentBrowserEnvironment);
68
+
69
+ findDOMNode._injectStack(function(inst) {
70
+ inst = getHostComponentFromComposite(inst);
71
+ return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
72
+ });
73
}
74
75
module.exports = {
src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
+1
-1
@@ -54,7 +54,7 @@ describe('findDOMNode', () => {
54
ReactDOM.unmountComponentAtNode(container);
55
56
expect(() => ReactDOM.findDOMNode(inst)).toThrowError(
57
- 'findDOMNode was called on an unmounted component.'
57
+ 'Unable to find node on an unmounted component.'
58
);
59
});
60
src/renderers/noop/ReactNoop.js
+3
-1
@@ -24,6 +24,7 @@ import type { UpdateQueue } from 'ReactFiberUpdateQueue';
24
import type { HostChildren } from 'ReactFiberReconciler';
25
26
var ReactFiberReconciler = require('ReactFiberReconciler');
27
+var ReactInstanceMap = require('ReactInstanceMap');
28
var {
29
AnimationPriority,
30
} = require('ReactPriorityLevel');
@@ -209,7 +210,8 @@ var ReactNoop = {
210
if (component.tag === TERMINAL_TAG || component.tag === TEXT_TAG) {
211
return component;
212
}
212
- return NoopRenderer.findHostInstance(component);
213
+ const inst = ReactInstanceMap.get(component);
214
+ return inst ? NoopRenderer.findHostInstance(inst) : null;
215
},
216
217
flushAnimationPri() {
src/renderers/shared/fiber/ReactFiberReconciler.js
+5
-5
@@ -78,7 +78,7 @@ export type Reconciler<C, I, TI> = {
78
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | TI | I | null),
79
80
// Use for findDOMNode/findHostNode. Legacy API.
81
- findHostInstance(component : ReactComponent<any, any, any>) : I | TI | null,
81
+ findHostInstance(component : Fiber) : I | TI | null,
82
};
83
84
module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) : Reconciler<C, I, TI> {
@@ -165,12 +165,12 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) :
165
return containerFiber.child.stateNode;
166
},
167
168
- findHostInstance(component : ReactComponent<any, any, any>) : I | TI | null {
169
- const fiber = findCurrentHostFiber(component);
170
- if (!fiber) {
168
+ findHostInstance(fiber : Fiber) : I | TI | null {
169
+ const hostFiber = findCurrentHostFiber(fiber);
170
+ if (!hostFiber) {
171
return null;
172
}
173
- return fiber.stateNode;
173
+ return hostFiber.stateNode;
174
},
175
176
};
src/renderers/shared/fiber/ReactFiberTreeReflection.js
+23
-15
@@ -16,6 +16,8 @@ import type { Fiber } from 'ReactFiber';
16
17
var ReactInstanceMap = require('ReactInstanceMap');
18
19
+var invariant = require('invariant');
20
+
21
var {
22
HostContainer,
23
HostComponent,
@@ -27,18 +29,22 @@ var {
29
Placement,
30
} = require('ReactTypeOfSideEffect');
31
30
-function isFiberMounted(fiber : Fiber) : boolean {
32
+var MOUNTING = 1;
33
+var MOUNTED = 2;
34
+var UNMOUNTED = 3;
35
+
36
+function isFiberMounted(fiber : Fiber) : number {
37
let node = fiber;
38
if (!fiber.alternate) {
39
// If there is no alternate, this might be a new tree that isn't inserted
40
// yet. If it is, then it will have a pending insertion effect on it.
41
if ((node.effectTag & Placement) !== NoEffect) {
36
- return false;
42
+ return MOUNTING;
43
}
44
while (node.return) {
45
node = node.return;
46
if ((node.effectTag & Placement) !== NoEffect) {
41
- return false;
47
+ return MOUNTING;
48
}
49
}
50
} else {
@@ -49,10 +55,11 @@ function isFiberMounted(fiber : Fiber) : boolean {
55
if (node.tag === HostContainer) {
56
// TODO: Check if this was a nested HostContainer when used with
57
// renderContainerIntoSubtree.
52
- return true;
58
+ return MOUNTED;
59
}
54
- // If we didn't hit the root, that means that we're in an disconnected tree.
55
- return false;
60
+ // If we didn't hit the root, that means that we're in an disconnected tree
61
+ // that has been unmounted.
62
+ return UNMOUNTED;
63
}
64
65
exports.isMounted = function(component : ReactComponent<any, any, any>) : boolean {
@@ -60,17 +67,18 @@ exports.isMounted = function(component : ReactComponent<any, any, any>) : boolea
67
if (!fiber) {
68
return false;
69
}
63
- return isFiberMounted(fiber);
70
+ return isFiberMounted(fiber) === MOUNTED;
71
};
72
66
-exports.findCurrentHostFiber = function(component : ReactComponent<any, any, any>) : Fiber | null {
67
- let parent = ReactInstanceMap.get(component);
68
- if (!parent) {
69
- return null;
70
- }
71
-
72
- if (!isFiberMounted(parent)) {
73
- // First check if this node itself is mounted.
73
+exports.findCurrentHostFiber = function(parent : Fiber) : Fiber | null {
74
+ // First check if this node itself is mounted.
75
+ const state = isFiberMounted(parent, true);
76
+ if (state === UNMOUNTED) {
77
+ invariant(
78
+ false,
79
+ 'Unable to find node on an unmounted component.'
80
+ );
81
+ } else if (state === MOUNTING) {
82
return null;
83
}
84