mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
DOM components as refs
Still missing: .props/.getDOMNode warnings.
This commit is contained in:
@@ -23,7 +23,6 @@ var ReactFragment = require('ReactFragment');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
|
||||
var getIteratorFn = require('getIteratorFn');
|
||||
var invariant = require('invariant');
|
||||
@@ -293,18 +292,10 @@ function checkPropTypes(componentName, propTypes, props, location) {
|
||||
* @param {ReactElement} element
|
||||
*/
|
||||
function validatePropTypes(element) {
|
||||
if (!(typeof element.type === 'string' ||
|
||||
typeof element.type === 'function')) {
|
||||
// This has already warned. Don't throw.
|
||||
var componentClass = element.type;
|
||||
if (typeof componentClass !== 'function') {
|
||||
return;
|
||||
}
|
||||
// Extract the component class from the element. Converts string types
|
||||
// to a composite class which may have propTypes.
|
||||
// TODO: Validating a string's propTypes is not decoupled from the
|
||||
// rendering target which is problematic.
|
||||
var componentClass = ReactNativeComponent.getComponentClassForElement(
|
||||
element
|
||||
);
|
||||
var name = componentClass.displayName || componentClass.name;
|
||||
if (componentClass.propTypes) {
|
||||
checkPropTypes(
|
||||
|
||||
@@ -17,7 +17,6 @@ var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var isNode = require('isNode');
|
||||
var warning = require('warning');
|
||||
|
||||
/**
|
||||
@@ -45,7 +44,7 @@ function findDOMNode(componentOrElement) {
|
||||
if (componentOrElement == null) {
|
||||
return null;
|
||||
}
|
||||
if (isNode(componentOrElement)) {
|
||||
if (componentOrElement.nodeType === 1) {
|
||||
return componentOrElement;
|
||||
}
|
||||
if (ReactInstanceMap.has(componentOrElement)) {
|
||||
|
||||
@@ -820,6 +820,10 @@ ReactDOMComponent.Mixin = {
|
||||
this._wrapperState = null;
|
||||
},
|
||||
|
||||
getPublicInstance: function() {
|
||||
return ReactMount.getNode(this._rootNodeID);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
|
||||
|
||||
@@ -19,18 +19,15 @@ var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
var HTMLDOMPropertyConfig = require('HTMLDOMPropertyConfig');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
|
||||
var ReactDOMComponent = require('ReactDOMComponent');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactDOMTextComponent = require('ReactDOMTextComponent');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactEventListener = require('ReactEventListener');
|
||||
var ReactInjection = require('ReactInjection');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactReconcileTransaction = require('ReactReconcileTransaction');
|
||||
var SelectEventPlugin = require('SelectEventPlugin');
|
||||
@@ -38,91 +35,6 @@ var ServerReactRootIndex = require('ServerReactRootIndex');
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig');
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
var canDefineProperty = false;
|
||||
try {
|
||||
Object.defineProperty({}, 'test', {get: function() {}});
|
||||
canDefineProperty = true;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
var deprecatedDOMMethods = [
|
||||
'isMounted', 'replaceProps', 'replaceState', 'setProps', 'setState',
|
||||
'forceUpdate',
|
||||
];
|
||||
|
||||
function getDeclarationErrorAddendum(domWrapperClass) {
|
||||
var internalInstance = ReactInstanceMap.get(domWrapperClass);
|
||||
var owner = internalInstance._currentElement._owner || null;
|
||||
if (owner) {
|
||||
var name = owner.getName();
|
||||
if (name) {
|
||||
return ' This DOM component was rendered by `' + name + '`.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function autoGenerateWrapperClass(type) {
|
||||
var wrapperClass = ReactClass.createClass({
|
||||
tagName: type.toUpperCase(),
|
||||
render: function() {
|
||||
// Copy owner down for debugging info
|
||||
var internalInstance = ReactInstanceMap.get(this);
|
||||
return new ReactElement(
|
||||
type,
|
||||
null, // key
|
||||
null, // ref
|
||||
internalInstance._currentElement._owner, // owner
|
||||
this._internalProps || this.props
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
if (canDefineProperty) {
|
||||
Object.defineProperty(wrapperClass.prototype, 'props', {
|
||||
enumerable: true,
|
||||
set: function(props) {
|
||||
this._internalProps = props;
|
||||
},
|
||||
get: function() {
|
||||
warning(
|
||||
false,
|
||||
'ReactDOMComponent.props: Do not access .props of a DOM ' +
|
||||
'component directly; instead, recreate the props as `render` ' +
|
||||
'did originally or use React.findDOMNode and read the DOM ' +
|
||||
'properties/attributes directly.%s',
|
||||
getDeclarationErrorAddendum(this)
|
||||
);
|
||||
return this._internalProps;
|
||||
},
|
||||
});
|
||||
|
||||
deprecatedDOMMethods.forEach(function(method) {
|
||||
var old = wrapperClass.prototype[method];
|
||||
Object.defineProperty(wrapperClass.prototype, method, {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
warning(
|
||||
false,
|
||||
'ReactDOMComponent.%s(): Do not access .%s() of a DOM ' +
|
||||
'component.%s',
|
||||
method,
|
||||
method,
|
||||
getDeclarationErrorAddendum(this)
|
||||
);
|
||||
return old;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return wrapperClass;
|
||||
}
|
||||
|
||||
var alreadyInjected = false;
|
||||
|
||||
function inject() {
|
||||
@@ -165,10 +77,6 @@ function inject() {
|
||||
ReactDOMTextComponent
|
||||
);
|
||||
|
||||
ReactInjection.NativeComponent.injectAutoWrapper(
|
||||
autoGenerateWrapperClass
|
||||
);
|
||||
|
||||
ReactInjection.Class.injectMixin(ReactBrowserComponentMixin);
|
||||
|
||||
ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
|
||||
|
||||
@@ -805,12 +805,12 @@ describe('ReactDOMComponent', function() {
|
||||
it('warns on invalid nesting at root', () => {
|
||||
spyOn(console, 'error');
|
||||
var p = document.createElement('p');
|
||||
React.render(<tr />, p);
|
||||
React.render(<span><p /></span>, p);
|
||||
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toBe(
|
||||
'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
|
||||
'<p>. See p > tr.'
|
||||
'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
|
||||
'of <p>. See p > ... > p.'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
@@ -123,9 +122,7 @@ var ReactCompositeComponentMixin = {
|
||||
var publicProps = this._processProps(this._currentElement.props);
|
||||
var publicContext = this._processContext(context);
|
||||
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
);
|
||||
var Component = this._currentElement.type;
|
||||
|
||||
// Initialize the public class
|
||||
var inst = new Component(publicProps, publicContext);
|
||||
@@ -235,8 +232,7 @@ var ReactCompositeComponentMixin = {
|
||||
var renderedElement = this._renderValidatedComponent();
|
||||
|
||||
this._renderedComponent = this._instantiateReactComponent(
|
||||
renderedElement,
|
||||
this._currentElement.type // The wrapping type
|
||||
renderedElement
|
||||
);
|
||||
|
||||
var markup = ReactReconciler.mountComponent(
|
||||
@@ -304,9 +300,7 @@ var ReactCompositeComponentMixin = {
|
||||
*/
|
||||
_maskContext: function(context) {
|
||||
var maskedContext = null;
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
);
|
||||
var Component = this._currentElement.type;
|
||||
var contextTypes = Component.contextTypes;
|
||||
if (!contextTypes) {
|
||||
return emptyObject;
|
||||
@@ -329,9 +323,7 @@ var ReactCompositeComponentMixin = {
|
||||
_processContext: function(context) {
|
||||
var maskedContext = this._maskContext(context);
|
||||
if (__DEV__) {
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
);
|
||||
var Component = this._currentElement.type;
|
||||
if (Component.contextTypes) {
|
||||
this._checkPropTypes(
|
||||
Component.contextTypes,
|
||||
@@ -349,9 +341,7 @@ var ReactCompositeComponentMixin = {
|
||||
* @private
|
||||
*/
|
||||
_processChildContext: function(currentContext) {
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
);
|
||||
var Component = this._currentElement.type;
|
||||
var inst = this._instance;
|
||||
var childContext = inst.getChildContext && inst.getChildContext();
|
||||
if (childContext) {
|
||||
@@ -392,9 +382,7 @@ var ReactCompositeComponentMixin = {
|
||||
*/
|
||||
_processProps: function(newProps) {
|
||||
if (__DEV__) {
|
||||
var Component = ReactNativeComponent.getComponentClassForElement(
|
||||
this._currentElement
|
||||
);
|
||||
var Component = this._currentElement.type;
|
||||
if (Component.propTypes) {
|
||||
this._checkPropTypes(
|
||||
Component.propTypes,
|
||||
@@ -695,8 +683,7 @@ var ReactCompositeComponentMixin = {
|
||||
ReactReconciler.unmountComponent(prevComponentInstance);
|
||||
|
||||
this._renderedComponent = this._instantiateReactComponent(
|
||||
nextRenderedElement,
|
||||
this._currentElement.type
|
||||
nextRenderedElement
|
||||
);
|
||||
var nextMarkup = ReactReconciler.mountComponent(
|
||||
this._renderedComponent,
|
||||
|
||||
@@ -36,11 +36,6 @@ var ReactNativeComponentInjection = {
|
||||
injectComponentClasses: function(componentClasses) {
|
||||
assign(tagToComponentClass, componentClasses);
|
||||
},
|
||||
// Temporary hack since we expect DOM refs to behave like composites,
|
||||
// for this release.
|
||||
injectAutoWrapper: function(wrapperFactory) {
|
||||
autoGenerateWrapperClass = wrapperFactory;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactTestUtils = require('ReactTestUtils');
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
@@ -63,11 +62,11 @@ describe('ReactInstanceHandles', function() {
|
||||
});
|
||||
}
|
||||
|
||||
function getNodeID(instance) {
|
||||
if (instance === null) {
|
||||
function getNodeID(el) {
|
||||
if (el === null) {
|
||||
return '';
|
||||
}
|
||||
return ReactInstanceMap.get(instance)._rootNodeID;
|
||||
return el.getAttribute('data-reactid');
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -389,8 +388,8 @@ describe('ReactInstanceHandles', function() {
|
||||
// Common ancestor with grandparent is the grandparent.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1_C1,
|
||||
com: parent.refs.P_P1_C1,
|
||||
two: parent.refs.P_P1,
|
||||
com: parent.refs.P_P1,
|
||||
},
|
||||
// Grantparent across subcomponent boundaries.
|
||||
{
|
||||
|
||||
@@ -89,7 +89,6 @@ var FriendsStatusDisplay = React.createClass({
|
||||
var statusDisplays =
|
||||
ReactInstanceMap.get(this)
|
||||
._renderedComponent
|
||||
._renderedComponent
|
||||
._renderedChildren;
|
||||
for (name in statusDisplays) {
|
||||
var child = statusDisplays[name];
|
||||
|
||||
@@ -60,11 +60,10 @@ function isInternalComponentType(type) {
|
||||
* Given a ReactNode, create an instance that will actually be mounted.
|
||||
*
|
||||
* @param {ReactNode} node
|
||||
* @param {*} parentCompositeType The composite type that resolved this.
|
||||
* @return {object} A new instance of the element's constructor.
|
||||
* @protected
|
||||
*/
|
||||
function instantiateReactComponent(node, parentCompositeType) {
|
||||
function instantiateReactComponent(node) {
|
||||
var instance;
|
||||
|
||||
if (node === null || node === false) {
|
||||
@@ -83,12 +82,8 @@ function instantiateReactComponent(node, parentCompositeType) {
|
||||
);
|
||||
|
||||
// Special case string values
|
||||
if (parentCompositeType === element.type &&
|
||||
typeof element.type === 'string') {
|
||||
// Avoid recursion if the wrapper renders itself.
|
||||
if (typeof element.type === 'string') {
|
||||
instance = ReactNativeComponent.createInternalComponent(element);
|
||||
// All native components are currently wrapped in a composite so we're
|
||||
// safe to assume that this is what we should instantiate.
|
||||
} else if (isInternalComponentType(element.type)) {
|
||||
// This is temporarily available for custom components that are not string
|
||||
// represenations. I.e. ART. Once those are updated to use the string
|
||||
|
||||
@@ -45,7 +45,7 @@ function findAllInRenderedTreeInternal(inst, test) {
|
||||
var publicInst = inst.getPublicInstance()
|
||||
var ret = test(publicInst) ? [publicInst] : [];
|
||||
if (ReactTestUtils.isDOMComponent(publicInst)) {
|
||||
var renderedChildren = inst._renderedComponent._renderedChildren;
|
||||
var renderedChildren = inst._renderedChildren;
|
||||
var key;
|
||||
for (key in renderedChildren) {
|
||||
if (!renderedChildren.hasOwnProperty(key)) {
|
||||
@@ -96,7 +96,7 @@ var ReactTestUtils = {
|
||||
isDOMComponent: function(inst) {
|
||||
// TODO: Fix this heuristic. It's just here because composites can currently
|
||||
// pretend to be DOM components.
|
||||
return !!(inst && inst.tagName && inst.getDOMNode);
|
||||
return !!(inst && inst.nodeType === 1 && inst.tagName);
|
||||
},
|
||||
|
||||
isDOMComponentElement: function(inst) {
|
||||
@@ -116,13 +116,15 @@ var ReactTestUtils = {
|
||||
},
|
||||
|
||||
isCompositeComponentWithType: function(inst, type) {
|
||||
if (!ReactTestUtils.isCompositeComponent(inst)) {
|
||||
return false;
|
||||
}
|
||||
var internalInstance = ReactInstanceMap.get(inst);
|
||||
var constructor = internalInstance
|
||||
._currentElement
|
||||
.type;
|
||||
|
||||
return !!(ReactTestUtils.isCompositeComponent(inst) &&
|
||||
(constructor === type));
|
||||
return (constructor === type);
|
||||
},
|
||||
|
||||
isCompositeComponentElement: function(inst) {
|
||||
@@ -256,7 +258,8 @@ var ReactTestUtils = {
|
||||
);
|
||||
if (all.length !== 1) {
|
||||
throw new Error(
|
||||
'Did not find exactly one match for componentType:' + componentType
|
||||
'Did not find exactly one match for componentType:' + componentType +
|
||||
' (found ' + all.length + ')'
|
||||
);
|
||||
}
|
||||
return all[0];
|
||||
|
||||
@@ -85,7 +85,7 @@ assign(reactComponentExpectInternal.prototype, {
|
||||
// change soon.
|
||||
this.toBeDOMComponent();
|
||||
var renderedChildren =
|
||||
this._instance._renderedComponent._renderedChildren || {};
|
||||
this._instance._renderedChildren || {};
|
||||
for (var name in renderedChildren) {
|
||||
if (!renderedChildren.hasOwnProperty(name)) {
|
||||
continue;
|
||||
@@ -101,7 +101,7 @@ assign(reactComponentExpectInternal.prototype, {
|
||||
|
||||
toBeDOMComponentWithChildCount: function(count) {
|
||||
this.toBeDOMComponent();
|
||||
var renderedChildren = this._instance._renderedComponent._renderedChildren;
|
||||
var renderedChildren = this._instance._renderedChildren;
|
||||
expect(renderedChildren).toBeTruthy();
|
||||
expect(Object.keys(renderedChildren).length).toBe(count);
|
||||
return this;
|
||||
@@ -109,7 +109,7 @@ assign(reactComponentExpectInternal.prototype, {
|
||||
|
||||
toBeDOMComponentWithNoChildren: function() {
|
||||
this.toBeDOMComponent();
|
||||
expect(this._instance._renderedComponent._renderedChildren).toBeFalsy();
|
||||
expect(this._instance._renderedChildren).toBeFalsy();
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user