@samitouri / QOS-React / commits / 3438d503db

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