mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge pull request #2375 from sebmarkbage/droplegacyfactories
Drop Legacy Factories
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactElementValidator = require('ReactElementValidator');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
|
||||
var mapObject = require('mapObject');
|
||||
|
||||
@@ -26,13 +25,9 @@ var mapObject = require('mapObject');
|
||||
*/
|
||||
function createDOMFactory(tag) {
|
||||
if (__DEV__) {
|
||||
return ReactLegacyElement.markNonLegacyFactory(
|
||||
ReactElementValidator.createFactory(tag)
|
||||
);
|
||||
return ReactElementValidator.createFactory(tag);
|
||||
}
|
||||
return ReactLegacyElement.markNonLegacyFactory(
|
||||
ReactElement.createFactory(tag)
|
||||
);
|
||||
return ReactElement.createFactory(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -114,24 +114,4 @@ describe('ReactDOM', function() {
|
||||
expect(element.type).toBe('div');
|
||||
expect(console.warn.argsForCall.length).toBe(0);
|
||||
});
|
||||
|
||||
it('warns but allow dom factories to be used in createFactory', function() {
|
||||
spyOn(console, 'warn');
|
||||
var factory = React.createFactory(React.DOM.div);
|
||||
expect(factory().type).toBe('div');
|
||||
expect(console.warn.argsForCall.length).toBe(1);
|
||||
expect(console.warn.argsForCall[0][0]).toContain(
|
||||
'Do not pass React.DOM.div'
|
||||
);
|
||||
});
|
||||
|
||||
it('warns but allow dom factories to be used in createElement', function() {
|
||||
spyOn(console, 'warn');
|
||||
var element = React.createElement(React.DOM.div);
|
||||
expect(element.type).toBe('div');
|
||||
expect(console.warn.argsForCall.length).toBe(1);
|
||||
expect(console.warn.argsForCall[0][0]).toContain(
|
||||
'Do not pass React.DOM.div'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,6 @@ var ReactDOM = require('ReactDOM');
|
||||
var ReactDOMComponent = require('ReactDOMComponent');
|
||||
var ReactDefaultInjection = require('ReactDefaultInjection');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
@@ -46,14 +45,6 @@ if (__DEV__) {
|
||||
createFactory = ReactElementValidator.createFactory;
|
||||
}
|
||||
|
||||
// TODO: Drop legacy elements once classes no longer export these factories
|
||||
createElement = ReactLegacyElement.wrapCreateElement(
|
||||
createElement
|
||||
);
|
||||
createFactory = ReactLegacyElement.wrapCreateFactory(
|
||||
createFactory
|
||||
);
|
||||
|
||||
var render = ReactPerf.measure('React', 'render', ReactMount.render);
|
||||
|
||||
var React = {
|
||||
@@ -77,7 +68,6 @@ var React = {
|
||||
renderToString: ReactServerRendering.renderToString,
|
||||
renderToStaticMarkup: ReactServerRendering.renderToStaticMarkup,
|
||||
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
|
||||
isValidClass: ReactLegacyElement.isValidClass,
|
||||
isValidElement: ReactElement.isValidElement,
|
||||
withContext: ReactContext.withContext,
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ var DOMProperty = require('DOMProperty');
|
||||
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
@@ -27,10 +26,6 @@ var invariant = require('invariant');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
var warning = require('warning');
|
||||
|
||||
var createElement = ReactLegacyElement.wrapCreateElement(
|
||||
ReactElement.createElement
|
||||
);
|
||||
|
||||
var SEPARATOR = ReactInstanceHandles.SEPARATOR;
|
||||
|
||||
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
|
||||
@@ -342,7 +337,7 @@ var ReactMount = {
|
||||
typeof nextElement === 'string' ?
|
||||
' Instead of passing an element string, make sure to instantiate ' +
|
||||
'it by passing it to React.createElement.' :
|
||||
ReactLegacyElement.isValidFactory(nextElement) ?
|
||||
typeof nextElement === 'function' ?
|
||||
' Instead of passing a component class, make sure to instantiate ' +
|
||||
'it by passing it to React.createElement.' :
|
||||
// Check if it quacks like a element
|
||||
@@ -394,7 +389,7 @@ var ReactMount = {
|
||||
* @return {ReactComponent} Component instance rendered in `container`.
|
||||
*/
|
||||
constructAndRenderComponent: function(constructor, props, container) {
|
||||
var element = createElement(constructor, props);
|
||||
var element = ReactElement.createElement(constructor, props);
|
||||
return ReactMount.render(element, container);
|
||||
},
|
||||
|
||||
|
||||
@@ -15,12 +15,10 @@ var AutoFocusMixin = require('AutoFocusMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
|
||||
var keyMirror = require('keyMirror');
|
||||
|
||||
// Store a reference to the <button> `ReactDOMComponent`. TODO: use string
|
||||
var button = ReactElement.createFactory(ReactDOM.button.type);
|
||||
var button = ReactElement.createFactory('button');
|
||||
|
||||
var mouseListenerNames = keyMirror({
|
||||
onClick: true,
|
||||
|
||||
@@ -16,10 +16,8 @@ var LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
|
||||
// Store a reference to the <form> `ReactDOMComponent`. TODO: use string
|
||||
var form = ReactElement.createFactory(ReactDOM.form.type);
|
||||
var form = ReactElement.createFactory('form');
|
||||
|
||||
/**
|
||||
* Since onSubmit doesn't bubble OR capture on the top level in IE8, we need
|
||||
|
||||
@@ -16,10 +16,8 @@ var LocalEventTrapMixin = require('LocalEventTrapMixin');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
|
||||
// Store a reference to the <img> `ReactDOMComponent`. TODO: use string
|
||||
var img = ReactElement.createFactory(ReactDOM.img.type);
|
||||
var img = ReactElement.createFactory('img');
|
||||
|
||||
/**
|
||||
* Since onLoad doesn't bubble OR capture on the top level in IE8, we need to
|
||||
|
||||
@@ -17,15 +17,13 @@ var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var invariant = require('invariant');
|
||||
|
||||
// Store a reference to the <input> `ReactDOMComponent`. TODO: use string
|
||||
var input = ReactElement.createFactory(ReactDOM.input.type);
|
||||
var input = ReactElement.createFactory('input');
|
||||
|
||||
var instancesByReactID = {};
|
||||
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
// Store a reference to the <option> `ReactDOMComponent`. TODO: use string
|
||||
var option = ReactElement.createFactory(ReactDOM.option.type);
|
||||
var option = ReactElement.createFactory('option');
|
||||
|
||||
/**
|
||||
* Implements an <option> native component that warns when `selected` is set.
|
||||
|
||||
@@ -16,13 +16,11 @@ var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
|
||||
// Store a reference to the <select> `ReactDOMComponent`. TODO: use string
|
||||
var select = ReactElement.createFactory(ReactDOM.select.type);
|
||||
var select = ReactElement.createFactory('select');
|
||||
|
||||
function updateWithPendingValueIfMounted() {
|
||||
/*jshint validthis:true */
|
||||
|
||||
@@ -17,7 +17,6 @@ var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
|
||||
var ReactClass = require('ReactClass');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
@@ -25,8 +24,7 @@ var invariant = require('invariant');
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
// Store a reference to the <textarea> `ReactDOMComponent`. TODO: use string
|
||||
var textarea = ReactElement.createFactory(ReactDOM.textarea.type);
|
||||
var textarea = ReactElement.createFactory('textarea');
|
||||
|
||||
function forceUpdateIfMounted() {
|
||||
/*jshint validthis:true */
|
||||
|
||||
+4
-10
@@ -13,8 +13,6 @@
|
||||
|
||||
var ReactCompositeComponent = require('ReactCompositeComponent');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactElementValidator = require('ReactElementValidator');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
|
||||
@@ -679,14 +677,10 @@ var ReactClass = {
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
return ReactLegacyElement.wrapFactory(
|
||||
ReactElementValidator.createFactory(Constructor)
|
||||
);
|
||||
}
|
||||
return ReactLegacyElement.wrapFactory(
|
||||
ReactElement.createFactory(Constructor)
|
||||
);
|
||||
// Legacy hook TODO: Warn if this is accessed
|
||||
Constructor.type = Constructor;
|
||||
|
||||
return Constructor;
|
||||
},
|
||||
|
||||
injection: {
|
||||
|
||||
@@ -171,6 +171,17 @@ var ReactCompositeComponentMixin = {
|
||||
this.props = this._processProps(this.props);
|
||||
|
||||
this.state = this.getInitialState ? this.getInitialState() : null;
|
||||
|
||||
if (__DEV__) {
|
||||
// We allow auto-mocks to proceed as if they're returning null.
|
||||
if (typeof this.state === 'undefined' &&
|
||||
this.getInitialState && this.getInitialState._isMockFunction) {
|
||||
// This is probably bad practice. Consider warning here and
|
||||
// deprecating this convenience.
|
||||
this.state = null;
|
||||
}
|
||||
}
|
||||
|
||||
invariant(
|
||||
typeof this.state === 'object' && !Array.isArray(this.state),
|
||||
'%s.getInitialState(): must return an object or null',
|
||||
@@ -675,6 +686,15 @@ var ReactCompositeComponentMixin = {
|
||||
ReactCurrentOwner.current = this;
|
||||
try {
|
||||
renderedComponent = this.render();
|
||||
if (__DEV__) {
|
||||
// We allow auto-mocks to proceed as if they're returning null.
|
||||
if (typeof renderedComponent === 'undefined' &&
|
||||
this.render._isMockFunction) {
|
||||
// This is probably bad practice. Consider warning here and
|
||||
// deprecating this convenience.
|
||||
renderedComponent = null;
|
||||
}
|
||||
}
|
||||
if (renderedComponent === null || renderedComponent === false) {
|
||||
renderedComponent = ReactEmptyComponent.getEmptyComponent();
|
||||
ReactEmptyComponent.registerNullComponentID(this._rootNodeID);
|
||||
@@ -781,4 +801,8 @@ var ReactCompositeComponent = {
|
||||
|
||||
};
|
||||
|
||||
// Temporary injection.
|
||||
// TODO: Delete this hack once implementation details are hidden.
|
||||
instantiateReactComponent._compositeBase = ReactCompositeComponentBase;
|
||||
|
||||
module.exports = ReactCompositeComponent;
|
||||
|
||||
@@ -199,6 +199,7 @@ ReactElement.createFactory = function(type) {
|
||||
// easily accessed on elements. E.g. <Foo />.type === Foo.type.
|
||||
// This should not be named `constructor` since this may not be the function
|
||||
// that created the element, and it may not even be a constructor.
|
||||
// Legacy hook TODO: Warn if this is accessed
|
||||
factory.type = type;
|
||||
return factory;
|
||||
};
|
||||
|
||||
@@ -273,6 +273,7 @@ var ReactElementValidator = {
|
||||
null,
|
||||
type
|
||||
);
|
||||
// Legacy hook TODO: Warn if this is accessed
|
||||
validatedFactory.type = type;
|
||||
return validatedFactory;
|
||||
}
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactLegacyElement
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var monitorCodeUse = require('monitorCodeUse');
|
||||
var warning = require('warning');
|
||||
|
||||
var legacyFactoryLogs = {};
|
||||
function warnForLegacyFactoryCall() {
|
||||
if (!ReactLegacyElementFactory._isLegacyCallWarningEnabled) {
|
||||
return;
|
||||
}
|
||||
var owner = ReactCurrentOwner.current;
|
||||
var name = owner && owner.constructor ? owner.constructor.displayName : '';
|
||||
if (!name) {
|
||||
name = 'Something';
|
||||
}
|
||||
if (legacyFactoryLogs.hasOwnProperty(name)) {
|
||||
return;
|
||||
}
|
||||
legacyFactoryLogs[name] = true;
|
||||
warning(
|
||||
false,
|
||||
name + ' is calling a React component directly. ' +
|
||||
'Use a factory or JSX instead. See: http://fb.me/react-legacyfactory'
|
||||
);
|
||||
monitorCodeUse('react_legacy_factory_call', { version: 3, name: name });
|
||||
}
|
||||
|
||||
function warnForPlainFunctionType(type) {
|
||||
var isReactClass =
|
||||
type.prototype &&
|
||||
typeof type.prototype.mountComponent === 'function' &&
|
||||
typeof type.prototype.receiveComponent === 'function';
|
||||
if (isReactClass) {
|
||||
warning(
|
||||
false,
|
||||
'Did not expect to get a React class here. Use `Component` instead ' +
|
||||
'of `Component.type` or `this.constructor`.'
|
||||
);
|
||||
} else {
|
||||
if (!type._reactWarnedForThisType) {
|
||||
try {
|
||||
type._reactWarnedForThisType = true;
|
||||
} catch (x) {
|
||||
// just incase this is a frozen object or some special object
|
||||
}
|
||||
monitorCodeUse(
|
||||
'react_non_component_in_jsx',
|
||||
{ version: 3, name: type.name }
|
||||
);
|
||||
}
|
||||
warning(
|
||||
false,
|
||||
'This JSX uses a plain function. Only React components are ' +
|
||||
'valid in React\'s JSX transform.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function warnForNonLegacyFactory(type) {
|
||||
warning(
|
||||
false,
|
||||
'Do not pass React.DOM.' + type.type + ' to JSX or createFactory. ' +
|
||||
'Use the string "' + type.type + '" instead.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer static properties from the source to the target. Functions are
|
||||
* rebound to have this reflect the original source.
|
||||
*/
|
||||
function proxyStaticMethods(target, source) {
|
||||
if (typeof source !== 'function') {
|
||||
return;
|
||||
}
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
var value = source[key];
|
||||
if (typeof value === 'function') {
|
||||
var bound = value.bind(source);
|
||||
// Copy any properties defined on the function, such as `isRequired` on
|
||||
// a PropTypes validator.
|
||||
for (var k in value) {
|
||||
if (value.hasOwnProperty(k)) {
|
||||
bound[k] = value[k];
|
||||
}
|
||||
}
|
||||
target[key] = bound;
|
||||
} else {
|
||||
target[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use an object instead of a boolean because booleans are ignored by our
|
||||
// mocking libraries when these factories gets mocked.
|
||||
var LEGACY_MARKER = {};
|
||||
var NON_LEGACY_MARKER = {};
|
||||
|
||||
var ReactLegacyElementFactory = {};
|
||||
|
||||
ReactLegacyElementFactory.wrapCreateFactory = function(createFactory) {
|
||||
var legacyCreateFactory = function(type) {
|
||||
if (typeof type !== 'function') {
|
||||
// Non-function types cannot be legacy factories
|
||||
return createFactory(type);
|
||||
}
|
||||
|
||||
if (type.isReactNonLegacyFactory) {
|
||||
// This is probably a factory created by ReactDOM we unwrap it to get to
|
||||
// the underlying string type. It shouldn't have been passed here so we
|
||||
// warn.
|
||||
if (__DEV__) {
|
||||
warnForNonLegacyFactory(type);
|
||||
}
|
||||
return createFactory(type.type);
|
||||
}
|
||||
|
||||
if (type.isReactLegacyFactory) {
|
||||
// This is probably a legacy factory created by ReactCompositeComponent.
|
||||
// We unwrap it to get to the underlying class.
|
||||
return createFactory(type.type);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
warnForPlainFunctionType(type);
|
||||
}
|
||||
|
||||
// Unless it's a legacy factory, then this is probably a plain function,
|
||||
// that is expecting to be invoked by JSX. We can just return it as is.
|
||||
return type;
|
||||
};
|
||||
return legacyCreateFactory;
|
||||
};
|
||||
|
||||
ReactLegacyElementFactory.wrapCreateElement = function(createElement) {
|
||||
var legacyCreateElement = function(type, props, children) {
|
||||
if (typeof type !== 'function') {
|
||||
// Non-function types cannot be legacy factories
|
||||
return createElement.apply(this, arguments);
|
||||
}
|
||||
|
||||
var args;
|
||||
|
||||
if (type.isReactNonLegacyFactory) {
|
||||
// This is probably a factory created by ReactDOM we unwrap it to get to
|
||||
// the underlying string type. It shouldn't have been passed here so we
|
||||
// warn.
|
||||
if (__DEV__) {
|
||||
warnForNonLegacyFactory(type);
|
||||
}
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = type.type;
|
||||
return createElement.apply(this, args);
|
||||
}
|
||||
|
||||
if (type.isReactLegacyFactory) {
|
||||
// This is probably a legacy factory created by ReactCompositeComponent.
|
||||
// We unwrap it to get to the underlying class.
|
||||
if (type._isMockFunction) {
|
||||
// If this is a mock function, people will expect it to be called. We
|
||||
// will actually call the original mock factory function instead. This
|
||||
// future proofs unit testing that assume that these are classes.
|
||||
type.type._mockedReactClassConstructor = type;
|
||||
}
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = type.type;
|
||||
return createElement.apply(this, args);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
warnForPlainFunctionType(type);
|
||||
}
|
||||
|
||||
// This is being called with a plain function we should invoke it
|
||||
// immediately as if this was used with legacy JSX.
|
||||
return type.apply(null, Array.prototype.slice.call(arguments, 1));
|
||||
};
|
||||
return legacyCreateElement;
|
||||
};
|
||||
|
||||
ReactLegacyElementFactory.wrapFactory = function(factory) {
|
||||
invariant(
|
||||
typeof factory === 'function',
|
||||
'This is suppose to accept a element factory'
|
||||
);
|
||||
var legacyElementFactory = function(config, children) {
|
||||
// This factory should not be called when JSX is used. Use JSX instead.
|
||||
if (__DEV__) {
|
||||
warnForLegacyFactoryCall();
|
||||
}
|
||||
return factory.apply(this, arguments);
|
||||
};
|
||||
proxyStaticMethods(legacyElementFactory, factory.type);
|
||||
legacyElementFactory.isReactLegacyFactory = LEGACY_MARKER;
|
||||
legacyElementFactory.type = factory.type;
|
||||
return legacyElementFactory;
|
||||
};
|
||||
|
||||
// This is used to mark a factory that will remain. E.g. we're allowed to call
|
||||
// it as a function. However, you're not suppose to pass it to createElement
|
||||
// or createFactory, so it will warn you if you do.
|
||||
ReactLegacyElementFactory.markNonLegacyFactory = function(factory) {
|
||||
factory.isReactNonLegacyFactory = NON_LEGACY_MARKER;
|
||||
return factory;
|
||||
};
|
||||
|
||||
// Checks if a factory function is actually a legacy factory pretending to
|
||||
// be a class.
|
||||
ReactLegacyElementFactory.isValidFactory = function(factory) {
|
||||
// TODO: This will be removed and moved into a class validator or something.
|
||||
return typeof factory === 'function' &&
|
||||
factory.isReactLegacyFactory === LEGACY_MARKER;
|
||||
};
|
||||
|
||||
ReactLegacyElementFactory.isValidClass = function(factory) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'isValidClass is deprecated and will be removed in a future release. ' +
|
||||
'Use a more specific validator instead.'
|
||||
);
|
||||
}
|
||||
return ReactLegacyElementFactory.isValidFactory(factory);
|
||||
};
|
||||
|
||||
ReactLegacyElementFactory._isLegacyCallWarningEnabled = true;
|
||||
|
||||
module.exports = ReactLegacyElementFactory;
|
||||
@@ -58,7 +58,7 @@ function createInstanceForTag(tag, props, parentType) {
|
||||
return new genericComponentClass(tag, props);
|
||||
}
|
||||
// Unwrap legacy factories
|
||||
return new componentClass.type(props);
|
||||
return new componentClass(props);
|
||||
}
|
||||
|
||||
var ReactNativeComponent = {
|
||||
|
||||
@@ -878,51 +878,6 @@ describe('ReactCompositeComponent', function() {
|
||||
expect(ReactMount.purgeID.callCount).toBe(4);
|
||||
});
|
||||
|
||||
it('should warn but detect valid CompositeComponent classes', function() {
|
||||
var warn = console.warn;
|
||||
console.warn = mocks.getMockFunction();
|
||||
|
||||
var Component = React.createClass({
|
||||
render: function() {
|
||||
return <div/>;
|
||||
}
|
||||
});
|
||||
|
||||
expect(React.isValidClass(Component)).toBe(true);
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(1);
|
||||
expect(console.warn.mock.calls[0][0]).toContain(
|
||||
'isValidClass is deprecated and will be removed in a future release'
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn but detect invalid CompositeComponent classes', function() {
|
||||
var warn = console.warn;
|
||||
console.warn = mocks.getMockFunction();
|
||||
|
||||
var FnComponent = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
var NullComponent = null;
|
||||
|
||||
var TrickFnComponent = function() {
|
||||
return true;
|
||||
};
|
||||
TrickFnComponent.componentConstructor = true;
|
||||
|
||||
expect(React.isValidClass(FnComponent)).toBe(false);
|
||||
expect(React.isValidClass(NullComponent)).toBe(false);
|
||||
expect(React.isValidClass(TrickFnComponent)).toBe(false);
|
||||
|
||||
expect(console.warn.mock.calls.length).toBe(3);
|
||||
console.warn.mock.calls.forEach(function(call) {
|
||||
expect(call[0]).toContain(
|
||||
'isValidClass is deprecated and will be removed in a future release'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should warn when shouldComponentUpdate() returns undefined', function() {
|
||||
var warn = console.warn;
|
||||
console.warn = mocks.getMockFunction();
|
||||
@@ -1282,8 +1237,8 @@ describe('ReactCompositeComponent', function() {
|
||||
expect(Component.ghi).toBe(null);
|
||||
expect(instance.constructor.jkl).toBe('mno');
|
||||
expect(Component.jkl).toBe('mno');
|
||||
expect(instance.constructor.pqr()).toBe(Component.type);
|
||||
expect(Component.pqr()).toBe(Component.type);
|
||||
expect(instance.constructor.pqr()).toBe(Component);
|
||||
expect(Component.pqr()).toBe(Component);
|
||||
});
|
||||
|
||||
it('should throw if a reserved property is in statics', function() {
|
||||
|
||||
@@ -152,8 +152,8 @@ describe('ReactCompositeComponent-mixin', function() {
|
||||
});
|
||||
|
||||
it('should validate prop types via mixins', function() {
|
||||
expect(TestComponent.type.propTypes).toBeDefined();
|
||||
expect(TestComponent.type.propTypes.value)
|
||||
expect(TestComponent.propTypes).toBeDefined();
|
||||
expect(TestComponent.propTypes.value)
|
||||
.toBe(mixinPropValidator);
|
||||
});
|
||||
|
||||
@@ -161,11 +161,11 @@ describe('ReactCompositeComponent-mixin', function() {
|
||||
// Sanity check...
|
||||
expect(componentPropValidator).toNotBe(mixinPropValidator);
|
||||
// Actually check...
|
||||
expect(TestComponentWithPropTypes.type.propTypes)
|
||||
expect(TestComponentWithPropTypes.propTypes)
|
||||
.toBeDefined();
|
||||
expect(TestComponentWithPropTypes.type.propTypes.value)
|
||||
expect(TestComponentWithPropTypes.propTypes.value)
|
||||
.toNotBe(mixinPropValidator);
|
||||
expect(TestComponentWithPropTypes.type.propTypes.value)
|
||||
expect(TestComponentWithPropTypes.propTypes.value)
|
||||
.toBe(componentPropValidator);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('ReactCompositeComponent-spec', function() {
|
||||
}
|
||||
});
|
||||
|
||||
expect(TestComponent.type.displayName)
|
||||
expect(TestComponent.displayName)
|
||||
.toBe('TestComponent');
|
||||
});
|
||||
|
||||
@@ -56,8 +56,8 @@ describe('ReactCompositeComponent-spec', function() {
|
||||
}
|
||||
});
|
||||
|
||||
expect(TestComponent.type.propTypes).toBeDefined();
|
||||
expect(TestComponent.type.propTypes.value)
|
||||
expect(TestComponent.propTypes).toBeDefined();
|
||||
expect(TestComponent.propTypes.value)
|
||||
.toBe(propValidator);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('ReactElement', function() {
|
||||
ComponentFactory = React.createClass({
|
||||
render: function() { return <div />; }
|
||||
});
|
||||
ComponentClass = ComponentFactory.type;
|
||||
ComponentClass = ComponentFactory;
|
||||
});
|
||||
|
||||
it('returns a complete element according to spec', function() {
|
||||
@@ -338,52 +338,6 @@ describe('ReactElement', function() {
|
||||
expect(ReactElement.isValidElement(Component)).toEqual(false);
|
||||
});
|
||||
|
||||
it('warns but allow a plain function in a factory to be invoked', function() {
|
||||
spyOn(console, 'warn');
|
||||
// This is a temporary helper to allow JSX with plain functions.
|
||||
// This allow you to track down these callers and replace them with regular
|
||||
// function calls.
|
||||
var factory = React.createFactory(function (x) {
|
||||
return 21 + x;
|
||||
});
|
||||
expect(factory(21)).toBe(42);
|
||||
expect(console.warn.argsForCall.length).toBe(1);
|
||||
expect(console.warn.argsForCall[0][0]).toContain(
|
||||
'This JSX uses a plain function.'
|
||||
);
|
||||
});
|
||||
|
||||
it('warns but allow a plain function to be immediately invoked', function() {
|
||||
spyOn(console, 'warn');
|
||||
var result = React.createElement(function (x, y) {
|
||||
return 21 + x + y;
|
||||
}, 11, 10);
|
||||
expect(result).toBe(42);
|
||||
expect(console.warn.argsForCall.length).toBe(1);
|
||||
expect(console.warn.argsForCall[0][0]).toContain(
|
||||
'This JSX uses a plain function.'
|
||||
);
|
||||
});
|
||||
|
||||
it('warns but does not fail on undefined results', function() {
|
||||
spyOn(console, 'warn');
|
||||
var fn = function () { };
|
||||
var result = React.createElement(fn, 1, 2, null);
|
||||
expect(result).toBe(undefined);
|
||||
expect(console.warn.argsForCall.length).toBe(1);
|
||||
expect(console.warn.argsForCall[0][0]).toContain(
|
||||
'This JSX uses a plain function.'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should expose the underlying class from a legacy factory', function() {
|
||||
var Legacy = React.createClass({ render: function() { } });
|
||||
var factory = React.createFactory(Legacy);
|
||||
expect(factory.type).toBe(Legacy.type);
|
||||
expect(factory().type).toBe(Legacy.type);
|
||||
});
|
||||
|
||||
it('allows the use of PropTypes validators in statics', function() {
|
||||
var Component = React.createClass({
|
||||
render: () => null,
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright 2013-2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var React;
|
||||
var ReactComponent;
|
||||
var ReactTestUtils;
|
||||
|
||||
var reactComponentExpect;
|
||||
var mocks;
|
||||
|
||||
var OriginalComponent;
|
||||
var AutoMockedComponent;
|
||||
var MockedComponent;
|
||||
|
||||
describe('ReactMockedComponent', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
mocks = require('mocks');
|
||||
|
||||
React = require('React');
|
||||
ReactComponent = require('ReactComponent');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
OriginalComponent = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return { foo: 'bar' };
|
||||
},
|
||||
|
||||
hasCustomMethod: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var metaData = mocks.getMetadata(OriginalComponent);
|
||||
|
||||
AutoMockedComponent = mocks.generateFromMetadata(metaData);
|
||||
MockedComponent = mocks.generateFromMetadata(metaData);
|
||||
|
||||
ReactTestUtils.mockComponent(MockedComponent);
|
||||
});
|
||||
|
||||
it('should allow an implicitly mocked component to be rendered', () => {
|
||||
ReactTestUtils.renderIntoDocument(<AutoMockedComponent />);
|
||||
});
|
||||
|
||||
it('should allow an implicitly mocked component to be updated', () => {
|
||||
var Wrapper = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return { foo: 1 };
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.setState({ foo: 2 });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <AutoMockedComponent prop={this.state.foo} />;
|
||||
}
|
||||
|
||||
});
|
||||
var instance = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
||||
instance.update();
|
||||
});
|
||||
|
||||
it('should find an implicitly mocked component in the tree', function() {
|
||||
var instance = ReactTestUtils.renderIntoDocument(
|
||||
<div><span><AutoMockedComponent prop="1" /></span></div>
|
||||
);
|
||||
var found = ReactTestUtils.findRenderedComponentWithType(
|
||||
instance,
|
||||
AutoMockedComponent
|
||||
);
|
||||
expect(typeof found).toBe('object');
|
||||
});
|
||||
|
||||
it('has custom methods on the implicitly mocked component', () => {
|
||||
var instance = ReactTestUtils.renderIntoDocument(<AutoMockedComponent />);
|
||||
expect(typeof instance.hasCustomMethod).toBe('function');
|
||||
});
|
||||
|
||||
it('should allow an explicitly mocked component to be rendered', () => {
|
||||
ReactTestUtils.renderIntoDocument(<MockedComponent />);
|
||||
});
|
||||
|
||||
it('should allow an explicitly mocked component to be updated', () => {
|
||||
var Wrapper = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return { foo: 1 };
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.setState({ foo: 2 });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <MockedComponent prop={this.state.foo} />;
|
||||
}
|
||||
|
||||
});
|
||||
var instance = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
||||
instance.update();
|
||||
});
|
||||
|
||||
it('should find an explicitly mocked component in the tree', function() {
|
||||
var instance = ReactTestUtils.renderIntoDocument(
|
||||
<div><span><MockedComponent prop="1" /></span></div>
|
||||
);
|
||||
var found = ReactTestUtils.findRenderedComponentWithType(
|
||||
instance,
|
||||
MockedComponent
|
||||
);
|
||||
expect(typeof found).toBe('object');
|
||||
});
|
||||
|
||||
it('has custom methods on the explicitly mocked component', () => {
|
||||
var instance = ReactTestUtils.renderIntoDocument(<MockedComponent />);
|
||||
expect(typeof instance.hasCustomMethod).toBe('function');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -15,9 +15,29 @@
|
||||
var warning = require('warning');
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactLegacyElement = require('ReactLegacyElement');
|
||||
var ReactNativeComponent = require('ReactNativeComponent');
|
||||
var ReactEmptyComponent = require('ReactEmptyComponent');
|
||||
|
||||
// This is temporary until we've hidden all the implementation details
|
||||
// TODO: Delete this hack once implementation details are hidden
|
||||
var publicAPIs = {
|
||||
forceUpdate: true,
|
||||
replaceState: true,
|
||||
setProps: true,
|
||||
setState: true,
|
||||
getDOMNode: true
|
||||
// Public APIs used internally:
|
||||
// isMounted: true,
|
||||
// replaceProps: true,
|
||||
};
|
||||
|
||||
function unmockImplementationDetails(mockInstance) {
|
||||
var ReactCompositeComponentBase = instantiateReactComponent._compositeBase;
|
||||
for (var key in ReactCompositeComponentBase.prototype) {
|
||||
if (!publicAPIs.hasOwnProperty(key)) {
|
||||
mockInstance[key] = ReactCompositeComponentBase.prototype[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an `element` create an instance that will actually be mounted.
|
||||
@@ -36,47 +56,6 @@ function instantiateReactComponent(element, parentCompositeType) {
|
||||
typeof element.type === 'string'),
|
||||
'Only functions or strings can be mounted as React components.'
|
||||
);
|
||||
|
||||
// Resolve mock instances
|
||||
if (element.type._mockedReactClassConstructor) {
|
||||
// If this is a mocked class, we treat the legacy factory as if it was the
|
||||
// class constructor for future proofing unit tests. Because this might
|
||||
// be mocked as a legacy factory, we ignore any warnings triggerd by
|
||||
// this temporary hack.
|
||||
ReactLegacyElement._isLegacyCallWarningEnabled = false;
|
||||
try {
|
||||
instance = new element.type._mockedReactClassConstructor(
|
||||
element.props
|
||||
);
|
||||
} finally {
|
||||
ReactLegacyElement._isLegacyCallWarningEnabled = true;
|
||||
}
|
||||
|
||||
// If the mock implementation was a legacy factory, then it returns a
|
||||
// element. We need to turn this into a real component instance.
|
||||
if (ReactElement.isValidElement(instance)) {
|
||||
instance = new instance.type(instance.props);
|
||||
}
|
||||
|
||||
var render = instance.render;
|
||||
if (!render) {
|
||||
// For auto-mocked factories, the prototype isn't shimmed and therefore
|
||||
// there is no render function on the instance. We replace the whole
|
||||
// component with an empty component instance instead.
|
||||
element = ReactEmptyComponent.getEmptyComponent();
|
||||
} else {
|
||||
if (render._isMockFunction && !render._getMockImplementation()) {
|
||||
// Auto-mocked components may have a prototype with a mocked render
|
||||
// function. For those, we'll need to mock the result of the render
|
||||
// since we consider undefined to be invalid results from render.
|
||||
render.mockImplementation(
|
||||
ReactEmptyComponent.getEmptyComponent
|
||||
);
|
||||
}
|
||||
instance.construct(element);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case string values
|
||||
@@ -92,6 +71,11 @@ function instantiateReactComponent(element, parentCompositeType) {
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (element.type._isMockFunction) {
|
||||
// TODO: Remove this special case
|
||||
unmockImplementationDetails(instance);
|
||||
}
|
||||
|
||||
warning(
|
||||
typeof instance.construct === 'function' &&
|
||||
typeof instance.mountComponent === 'function' &&
|
||||
|
||||
@@ -55,7 +55,7 @@ var ReactTestUtils = {
|
||||
isElementOfType: function(inst, convenienceConstructor) {
|
||||
return (
|
||||
ReactElement.isValidElement(inst) &&
|
||||
inst.type === convenienceConstructor.type
|
||||
inst.type === convenienceConstructor
|
||||
);
|
||||
},
|
||||
|
||||
@@ -76,7 +76,7 @@ var ReactTestUtils = {
|
||||
|
||||
isCompositeComponentWithType: function(inst, type) {
|
||||
return !!(ReactTestUtils.isCompositeComponent(inst) &&
|
||||
(inst.constructor === type.type));
|
||||
(inst.constructor === type));
|
||||
},
|
||||
|
||||
isCompositeComponentElement: function(inst) {
|
||||
@@ -231,21 +231,14 @@ var ReactTestUtils = {
|
||||
mockComponent: function(module, mockTagName) {
|
||||
mockTagName = mockTagName || module.mockTagName || "div";
|
||||
|
||||
var ConvenienceConstructor = React.createClass({
|
||||
render: function() {
|
||||
return React.createElement(
|
||||
mockTagName,
|
||||
null,
|
||||
this.props.children
|
||||
);
|
||||
}
|
||||
module.prototype.render.mockImplementation(function() {
|
||||
return React.createElement(
|
||||
mockTagName,
|
||||
null,
|
||||
this.props.children
|
||||
);
|
||||
});
|
||||
|
||||
module.mockImplementation(ConvenienceConstructor);
|
||||
|
||||
module.type = ConvenienceConstructor.type;
|
||||
module.isReactLegacyFactory = true;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -97,12 +97,9 @@ assign(reactComponentExpect.prototype, {
|
||||
|
||||
// Matchers ------------------------------------------------------------------
|
||||
|
||||
toBeComponentOfType: function(convenienceConstructor) {
|
||||
var type = typeof convenienceConstructor === 'string' ?
|
||||
convenienceConstructor :
|
||||
convenienceConstructor.type;
|
||||
toBeComponentOfType: function(constructor) {
|
||||
expect(
|
||||
this.instance()._currentElement.type === type
|
||||
this.instance()._currentElement.type === constructor
|
||||
).toBe(true);
|
||||
return this;
|
||||
},
|
||||
@@ -119,10 +116,10 @@ assign(reactComponentExpect.prototype, {
|
||||
return this;
|
||||
},
|
||||
|
||||
toBeCompositeComponentWithType: function(convenienceConstructor) {
|
||||
toBeCompositeComponentWithType: function(constructor) {
|
||||
this.toBeCompositeComponent();
|
||||
expect(
|
||||
this.instance()._currentElement.type === convenienceConstructor.type
|
||||
this.instance()._currentElement.type === constructor
|
||||
).toBe(true);
|
||||
return this;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user