Skip to content

Commit 3438d50

Browse files
authored
Make findDOMNode generic (facebook#8348)
1 parent 77c890d commit 3438d50

File tree

9 files changed

+84
-65
lines changed

9 files changed

+84
-65
lines changed

scripts/fiber/tests-failing.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ src/renderers/dom/stack/client/__tests__/ReactRenderDocument-test.js
108108
* should throw on full document render w/ no markup
109109
* supports findDOMNode on full-page components
110110

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-
115111
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
116112
* should generate simple markup
117113
* should generate simple markup for self-closing tags

scripts/fiber/tests-passing.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,8 @@ src/renderers/dom/stack/client/__tests__/ReactMountDestruction-test.js
908908
src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
909909
* findDOMNode should return null if passed null
910910
* findDOMNode should find dom element
911+
* findDOMNode should reject random objects
912+
* findDOMNode should reject unmounted objects with render func
911913
* findDOMNode should not throw an error when called within a component that is not mounted
912914

913915
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js

src/renderers/dom/fiber/ReactDOMFiber.js

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
'use strict';
1414

15+
import type { Fiber } from 'ReactFiber';
1516
import type { HostChildren } from 'ReactFiberReconciler';
1617

1718
var ReactControlledComponent = require('ReactControlledComponent');
@@ -21,11 +22,7 @@ var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
2122
var ReactDOMFiberComponent = require('ReactDOMFiberComponent');
2223
var ReactDOMInjection = require('ReactDOMInjection');
2324

24-
ReactDOMInjection.inject();
25-
ReactControlledComponent.injection.injectFiberControlledHostComponent(
26-
ReactDOMFiberComponent
27-
);
28-
25+
var findDOMNode = require('findDOMNode');
2926
var warning = require('warning');
3027

3128
var {
@@ -35,6 +32,14 @@ var {
3532
} = ReactDOMFiberComponent;
3633
var { precacheFiberNode } = ReactDOMComponentTree;
3734

35+
ReactDOMInjection.inject();
36+
ReactControlledComponent.injection.injectFiberControlledHostComponent(
37+
ReactDOMFiberComponent
38+
);
39+
findDOMNode._injectFiber(function(fiber: Fiber) {
40+
return DOMRenderer.findHostInstance(fiber);
41+
});
42+
3843
type DOMContainerElement = Element & { _reactRootContainer: ?Object };
3944

4045
type Container = Element;
@@ -167,17 +172,7 @@ var ReactDOM = {
167172
}
168173
},
169174

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,
181176

182177
unstable_batchedUpdates<A>(fn : () => A) : A {
183178
return DOMRenderer.batchedUpdates(fn);

src/renderers/dom/stack/client/findDOMNode.js renamed to src/renderers/dom/shared/findDOMNode.js

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,59 +7,59 @@
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*
99
* @providesModule findDOMNode
10+
* @flow
1011
*/
1112

12-
'use strict';
13-
1413
var ReactCurrentOwner = require('ReactCurrentOwner');
15-
var ReactDOMComponentTree = require('ReactDOMComponentTree');
1614
var ReactInstanceMap = require('ReactInstanceMap');
1715

18-
var getHostComponentFromComposite = require('getHostComponentFromComposite');
16+
var getComponentName = require('getComponentName');
1917
var invariant = require('invariant');
2018
var warning = require('warning');
2119

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 {
3128
if (__DEV__) {
3229
var owner = ReactCurrentOwner.current;
33-
if (owner !== null) {
30+
if (owner !== null && '_warnedAboutRefsInRender' in owner) {
3431
warning(
35-
owner._warnedAboutRefsInRender,
32+
(owner: any)._warnedAboutRefsInRender,
3633
'%s is accessing findDOMNode inside its render(). ' +
3734
'render() should be a pure function of props and state. It should ' +
3835
'never access something that requires stale data from the previous ' +
3936
'render, such as refs. Move this logic to componentDidMount and ' +
4037
'componentDidUpdate instead.',
41-
owner.getName() || 'A component'
38+
getComponentName(owner) || 'A component'
4239
);
43-
owner._warnedAboutRefsInRender = true;
40+
(owner: any)._warnedAboutRefsInRender = true;
4441
}
4542
}
4643
if (componentOrElement == null) {
4744
return null;
4845
}
49-
if (componentOrElement.nodeType === 1) {
50-
return componentOrElement;
46+
if ((componentOrElement: any).nodeType === 1) {
47+
return (componentOrElement: any);
5148
}
5249

5350
var inst = ReactInstanceMap.get(componentOrElement);
5451
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+
}
5757
}
5858

5959
if (typeof componentOrElement.render === 'function') {
6060
invariant(
6161
false,
62-
'findDOMNode was called on an unmounted component.'
62+
'Unable to find node on an unmounted component.'
6363
);
6464
} else {
6565
invariant(
@@ -68,6 +68,13 @@ function findDOMNode(componentOrElement) {
6868
Object.keys(componentOrElement)
6969
);
7070
}
71-
}
71+
};
72+
73+
findDOMNode._injectFiber = function(fn) {
74+
findFiber = fn;
75+
};
76+
findDOMNode._injectStack = function(fn) {
77+
findStack = fn;
78+
};
7279

7380
module.exports = findDOMNode;

src/renderers/dom/stack/client/ReactDOMStackInjection.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment');
1515
var ReactComponentBrowserEnvironment =
1616
require('ReactComponentBrowserEnvironment');
1717
var ReactDOMComponent = require('ReactDOMComponent');
18+
var ReactDOMComponentTree = require('ReactDOMComponentTree');
1819
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
1920
var ReactDOMTextComponent = require('ReactDOMTextComponent');
2021
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
@@ -24,6 +25,9 @@ var ReactHostComponent = require('ReactHostComponent');
2425
var ReactReconcileTransaction = require('ReactReconcileTransaction');
2526
var ReactUpdates = require('ReactUpdates');
2627

28+
var findDOMNode = require('findDOMNode');
29+
var getHostComponentFromComposite = require('getHostComponentFromComposite');
30+
2731
var alreadyInjected = false;
2832

2933
function inject() {
@@ -61,6 +65,11 @@ function inject() {
6165
);
6266

6367
ReactComponentEnvironment.injection.injectEnvironment(ReactComponentBrowserEnvironment);
68+
69+
findDOMNode._injectStack(function(inst) {
70+
inst = getHostComponentFromComposite(inst);
71+
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
72+
});
6473
}
6574

6675
module.exports = {

src/renderers/dom/stack/client/__tests__/findDOMNode-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('findDOMNode', () => {
5454
ReactDOM.unmountComponentAtNode(container);
5555

5656
expect(() => ReactDOM.findDOMNode(inst)).toThrowError(
57-
'findDOMNode was called on an unmounted component.'
57+
'Unable to find node on an unmounted component.'
5858
);
5959
});
6060

src/renderers/noop/ReactNoop.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type { UpdateQueue } from 'ReactFiberUpdateQueue';
2424
import type { HostChildren } from 'ReactFiberReconciler';
2525

2626
var ReactFiberReconciler = require('ReactFiberReconciler');
27+
var ReactInstanceMap = require('ReactInstanceMap');
2728
var {
2829
AnimationPriority,
2930
} = require('ReactPriorityLevel');
@@ -209,7 +210,8 @@ var ReactNoop = {
209210
if (component.tag === TERMINAL_TAG || component.tag === TEXT_TAG) {
210211
return component;
211212
}
212-
return NoopRenderer.findHostInstance(component);
213+
const inst = ReactInstanceMap.get(component);
214+
return inst ? NoopRenderer.findHostInstance(inst) : null;
213215
},
214216

215217
flushAnimationPri() {

src/renderers/shared/fiber/ReactFiberReconciler.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export type Reconciler<C, I, TI> = {
7878
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | TI | I | null),
7979

8080
// Use for findDOMNode/findHostNode. Legacy API.
81-
findHostInstance(component : ReactComponent<any, any, any>) : I | TI | null,
81+
findHostInstance(component : Fiber) : I | TI | null,
8282
};
8383

8484
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>) :
165165
return containerFiber.child.stateNode;
166166
},
167167

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) {
171171
return null;
172172
}
173-
return fiber.stateNode;
173+
return hostFiber.stateNode;
174174
},
175175

176176
};

src/renderers/shared/fiber/ReactFiberTreeReflection.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import type { Fiber } from 'ReactFiber';
1616

1717
var ReactInstanceMap = require('ReactInstanceMap');
1818

19+
var invariant = require('invariant');
20+
1921
var {
2022
HostContainer,
2123
HostComponent,
@@ -27,18 +29,22 @@ var {
2729
Placement,
2830
} = require('ReactTypeOfSideEffect');
2931

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 {
3137
let node = fiber;
3238
if (!fiber.alternate) {
3339
// If there is no alternate, this might be a new tree that isn't inserted
3440
// yet. If it is, then it will have a pending insertion effect on it.
3541
if ((node.effectTag & Placement) !== NoEffect) {
36-
return false;
42+
return MOUNTING;
3743
}
3844
while (node.return) {
3945
node = node.return;
4046
if ((node.effectTag & Placement) !== NoEffect) {
41-
return false;
47+
return MOUNTING;
4248
}
4349
}
4450
} else {
@@ -49,28 +55,30 @@ function isFiberMounted(fiber : Fiber) : boolean {
4955
if (node.tag === HostContainer) {
5056
// TODO: Check if this was a nested HostContainer when used with
5157
// renderContainerIntoSubtree.
52-
return true;
58+
return MOUNTED;
5359
}
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;
5663
}
5764

5865
exports.isMounted = function(component : ReactComponent<any, any, any>) : boolean {
5966
var fiber : ?Fiber = ReactInstanceMap.get(component);
6067
if (!fiber) {
6168
return false;
6269
}
63-
return isFiberMounted(fiber);
70+
return isFiberMounted(fiber) === MOUNTED;
6471
};
6572

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) {
7482
return null;
7583
}
7684

0 commit comments

Comments
 (0)