Make findDOMNode generic (#8348)

This commit is contained in:
Ben Alpert
2016-11-21 17:13:26 +00:00
committed by GitHub
parent 77c890dfa2
commit 3438d503db
9 changed files with 84 additions and 65 deletions
-4
View File
@@ -108,10 +108,6 @@ src/renderers/dom/stack/client/__tests__/ReactRenderDocument-test.js
* should throw on full document render w/ no markup
* supports findDOMNode on full-page components
src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
* findDOMNode should reject random objects
* findDOMNode should reject unmounted objects with render func
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
* should generate simple markup
* should generate simple markup for self-closing tags
+2
View File
@@ -908,6 +908,8 @@ src/renderers/dom/stack/client/__tests__/ReactMountDestruction-test.js
src/renderers/dom/stack/client/__tests__/findDOMNode-test.js
* findDOMNode should return null if passed null
* findDOMNode should find dom element
* findDOMNode should reject random objects
* findDOMNode should reject unmounted objects with render func
* findDOMNode should not throw an error when called within a component that is not mounted
src/renderers/dom/stack/server/__tests__/ReactServerRendering-test.js
+11 -16
View File
@@ -12,6 +12,7 @@
'use strict';
import type { Fiber } from 'ReactFiber';
import type { HostChildren } from 'ReactFiberReconciler';
var ReactControlledComponent = require('ReactControlledComponent');
@@ -21,11 +22,7 @@ var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactDOMFiberComponent = require('ReactDOMFiberComponent');
var ReactDOMInjection = require('ReactDOMInjection');
ReactDOMInjection.inject();
ReactControlledComponent.injection.injectFiberControlledHostComponent(
ReactDOMFiberComponent
);
var findDOMNode = require('findDOMNode');
var warning = require('warning');
var {
@@ -35,6 +32,14 @@ var {
} = ReactDOMFiberComponent;
var { precacheFiberNode } = ReactDOMComponentTree;
ReactDOMInjection.inject();
ReactControlledComponent.injection.injectFiberControlledHostComponent(
ReactDOMFiberComponent
);
findDOMNode._injectFiber(function(fiber: Fiber) {
return DOMRenderer.findHostInstance(fiber);
});
type DOMContainerElement = Element & { _reactRootContainer: ?Object };
type Container = Element;
@@ -167,17 +172,7 @@ var ReactDOM = {
}
},
findDOMNode(componentOrElement : Element | ?ReactComponent<any, any, any>) : null | Element | Text {
if (componentOrElement == null) {
return null;
}
// Unsound duck typing.
const component = (componentOrElement : any);
if (component.nodeType === 1) {
return component;
}
return DOMRenderer.findHostInstance(component);
},
findDOMNode: findDOMNode,
unstable_batchedUpdates<A>(fn : () => A) : A {
return DOMRenderer.batchedUpdates(fn);
@@ -7,59 +7,59 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule findDOMNode
* @flow
*/
'use strict';
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstanceMap = require('ReactInstanceMap');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
var getComponentName = require('getComponentName');
var invariant = require('invariant');
var warning = require('warning');
/**
* Returns the DOM node rendered by this element.
*
* See https://facebook.github.io/react/docs/react-dom.html#finddomnode
*
* @param {ReactComponent|DOMElement} componentOrElement
* @return {?DOMElement} The root node of this element.
*/
function findDOMNode(componentOrElement) {
let findFiber = function(arg) {
invariant(false, 'Missing injection for fiber findDOMNode');
};
let findStack = function(arg) {
invariant(false, 'Missing injection for stack findDOMNode');
};
const findDOMNode = function(componentOrElement : Element | ?ReactComponent<any, any, any>) : null | Element | Text {
if (__DEV__) {
var owner = ReactCurrentOwner.current;
if (owner !== null) {
if (owner !== null && '_warnedAboutRefsInRender' in owner) {
warning(
owner._warnedAboutRefsInRender,
(owner: any)._warnedAboutRefsInRender,
'%s is accessing findDOMNode inside its render(). ' +
'render() should be a pure function of props and state. It should ' +
'never access something that requires stale data from the previous ' +
'render, such as refs. Move this logic to componentDidMount and ' +
'componentDidUpdate instead.',
owner.getName() || 'A component'
getComponentName(owner) || 'A component'
);
owner._warnedAboutRefsInRender = true;
(owner: any)._warnedAboutRefsInRender = true;
}
}
if (componentOrElement == null) {
return null;
}
if (componentOrElement.nodeType === 1) {
return componentOrElement;
if ((componentOrElement: any).nodeType === 1) {
return (componentOrElement: any);
}
var inst = ReactInstanceMap.get(componentOrElement);
if (inst) {
inst = getHostComponentFromComposite(inst);
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
if (typeof inst.tag === 'number') {
return findFiber(inst);
} else {
return findStack(inst);
}
}
if (typeof componentOrElement.render === 'function') {
invariant(
false,
'findDOMNode was called on an unmounted component.'
'Unable to find node on an unmounted component.'
);
} else {
invariant(
@@ -68,6 +68,13 @@ function findDOMNode(componentOrElement) {
Object.keys(componentOrElement)
);
}
}
};
findDOMNode._injectFiber = function(fn) {
findFiber = fn;
};
findDOMNode._injectStack = function(fn) {
findStack = fn;
};
module.exports = findDOMNode;
@@ -15,6 +15,7 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
var ReactDOMComponent = require('ReactDOMComponent');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMEmptyComponent = require('ReactDOMEmptyComponent');
var ReactDOMTextComponent = require('ReactDOMTextComponent');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
@@ -24,6 +25,9 @@ var ReactHostComponent = require('ReactHostComponent');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
var ReactUpdates = require('ReactUpdates');
var findDOMNode = require('findDOMNode');
var getHostComponentFromComposite = require('getHostComponentFromComposite');
var alreadyInjected = false;
function inject() {
@@ -61,6 +65,11 @@ function inject() {
);
ReactComponentEnvironment.injection.injectEnvironment(ReactComponentBrowserEnvironment);
findDOMNode._injectStack(function(inst) {
inst = getHostComponentFromComposite(inst);
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
});
}
module.exports = {
@@ -54,7 +54,7 @@ describe('findDOMNode', () => {
ReactDOM.unmountComponentAtNode(container);
expect(() => ReactDOM.findDOMNode(inst)).toThrowError(
'findDOMNode was called on an unmounted component.'
'Unable to find node on an unmounted component.'
);
});
+3 -1
View File
@@ -24,6 +24,7 @@ import type { UpdateQueue } from 'ReactFiberUpdateQueue';
import type { HostChildren } from 'ReactFiberReconciler';
var ReactFiberReconciler = require('ReactFiberReconciler');
var ReactInstanceMap = require('ReactInstanceMap');
var {
AnimationPriority,
} = require('ReactPriorityLevel');
@@ -209,7 +210,8 @@ var ReactNoop = {
if (component.tag === TERMINAL_TAG || component.tag === TEXT_TAG) {
return component;
}
return NoopRenderer.findHostInstance(component);
const inst = ReactInstanceMap.get(component);
return inst ? NoopRenderer.findHostInstance(inst) : null;
},
flushAnimationPri() {
@@ -78,7 +78,7 @@ export type Reconciler<C, I, TI> = {
getPublicRootInstance(container : OpaqueNode) : (ReactComponent<any, any, any> | TI | I | null),
// Use for findDOMNode/findHostNode. Legacy API.
findHostInstance(component : ReactComponent<any, any, any>) : I | TI | null,
findHostInstance(component : Fiber) : I | TI | null,
};
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>) :
return containerFiber.child.stateNode;
},
findHostInstance(component : ReactComponent<any, any, any>) : I | TI | null {
const fiber = findCurrentHostFiber(component);
if (!fiber) {
findHostInstance(fiber : Fiber) : I | TI | null {
const hostFiber = findCurrentHostFiber(fiber);
if (!hostFiber) {
return null;
}
return fiber.stateNode;
return hostFiber.stateNode;
},
};
@@ -16,6 +16,8 @@ import type { Fiber } from 'ReactFiber';
var ReactInstanceMap = require('ReactInstanceMap');
var invariant = require('invariant');
var {
HostContainer,
HostComponent,
@@ -27,18 +29,22 @@ var {
Placement,
} = require('ReactTypeOfSideEffect');
function isFiberMounted(fiber : Fiber) : boolean {
var MOUNTING = 1;
var MOUNTED = 2;
var UNMOUNTED = 3;
function isFiberMounted(fiber : Fiber) : number {
let node = fiber;
if (!fiber.alternate) {
// If there is no alternate, this might be a new tree that isn't inserted
// yet. If it is, then it will have a pending insertion effect on it.
if ((node.effectTag & Placement) !== NoEffect) {
return false;
return MOUNTING;
}
while (node.return) {
node = node.return;
if ((node.effectTag & Placement) !== NoEffect) {
return false;
return MOUNTING;
}
}
} else {
@@ -49,10 +55,11 @@ function isFiberMounted(fiber : Fiber) : boolean {
if (node.tag === HostContainer) {
// TODO: Check if this was a nested HostContainer when used with
// renderContainerIntoSubtree.
return true;
return MOUNTED;
}
// If we didn't hit the root, that means that we're in an disconnected tree.
return false;
// If we didn't hit the root, that means that we're in an disconnected tree
// that has been unmounted.
return UNMOUNTED;
}
exports.isMounted = function(component : ReactComponent<any, any, any>) : boolean {
@@ -60,17 +67,18 @@ exports.isMounted = function(component : ReactComponent<any, any, any>) : boolea
if (!fiber) {
return false;
}
return isFiberMounted(fiber);
return isFiberMounted(fiber) === MOUNTED;
};
exports.findCurrentHostFiber = function(component : ReactComponent<any, any, any>) : Fiber | null {
let parent = ReactInstanceMap.get(component);
if (!parent) {
return null;
}
if (!isFiberMounted(parent)) {
// First check if this node itself is mounted.
exports.findCurrentHostFiber = function(parent : Fiber) : Fiber | null {
// First check if this node itself is mounted.
const state = isFiberMounted(parent, true);
if (state === UNMOUNTED) {
invariant(
false,
'Unable to find node on an unmounted component.'
);
} else if (state === MOUNTING) {
return null;
}