diff --git a/mocks/ReactElementTestChild.js b/mocks/ReactElementTestChild.js index f2c94d5dd6885..2e9d076933047 100644 --- a/mocks/ReactElementTestChild.js +++ b/mocks/ReactElementTestChild.js @@ -13,10 +13,10 @@ var React = require('React'); -var Child = React.createClass({ - render: function() { +class Child extends React.Component { + render() { return React.createElement('div'); - }, -}); + } +} module.exports = Child; diff --git a/mocks/ReactMockedComponentTestComponent.js b/mocks/ReactMockedComponentTestComponent.js index 903006487374f..285265df14876 100644 --- a/mocks/ReactMockedComponentTestComponent.js +++ b/mocks/ReactMockedComponentTestComponent.js @@ -13,23 +13,18 @@ var React = require('React'); -var ReactMockedComponentTestComponent = React.createClass({ - getDefaultProps: function() { - return {bar: 'baz'}; - }, +class ReactMockedComponentTestComponent extends React.Component { + state = {foo: 'bar'}; - getInitialState: function() { - return {foo: 'bar'}; - }, - - hasCustomMethod: function() { + hasCustomMethod() { return true; - }, + } - render: function() { + render() { return ; - }, + } -}); +} +ReactMockedComponentTestComponent.defaultProps = {bar: 'baz'}; module.exports = ReactMockedComponentTestComponent; diff --git a/package.json b/package.json index e79c5278772ed..c4ea09e12a682 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "merge-stream": "^1.0.0", "object-assign": "^4.1.1", "platform": "^1.1.0", + "react-create-class": "15.5.0-alpha.2", "run-sequence": "^1.1.4", "through2": "^2.0.0", "tmp": "~0.0.28", diff --git a/src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js b/src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js deleted file mode 100644 index 2f5c7a36c040c..0000000000000 --- a/src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright 2015-present, 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 ReactComponentWithPureRenderMixin; -var ReactTestUtils; - -describe('ReactComponentWithPureRenderMixin', () => { - - beforeEach(() => { - React = require('React'); - ReactComponentWithPureRenderMixin = - require('ReactComponentWithPureRenderMixin'); - ReactTestUtils = require('ReactTestUtils'); - }); - - it('provides a default shouldComponentUpdate implementation', () => { - var renderCalls = 0; - class PlasticWrap extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { - color: 'green', - }; - } - - render() { - return ( - - ); - } - } - - var Apple = React.createClass({ - mixins: [ReactComponentWithPureRenderMixin], - - getInitialState: function() { - return { - cut: false, - slices: 1, - }; - }, - - cut: function() { - this.setState({ - cut: true, - slices: 10, - }); - }, - - eatSlice: function() { - this.setState({ - slices: this.state.slices - 1, - }); - }, - - render: function() { - renderCalls++; - return
; - }, - }); - - var instance = ReactTestUtils.renderIntoDocument(); - expect(renderCalls).toBe(1); - - // Do not re-render based on props - instance.setState({color: 'green'}); - expect(renderCalls).toBe(1); - - // Re-render based on props - instance.setState({color: 'red'}); - expect(renderCalls).toBe(2); - - // Re-render base on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // No re-render based on state - instance.refs.apple.cut(); - expect(renderCalls).toBe(3); - - // Re-render based on state again - instance.refs.apple.eatSlice(); - expect(renderCalls).toBe(4); - }); - - it('does not do a deep comparison', () => { - function getInitialState() { - return { - foo: [1, 2, 3], - bar: {a: 4, b: 5, c: 6}, - }; - } - - var renderCalls = 0; - var initialSettings = getInitialState(); - - var Component = React.createClass({ - mixins: [ReactComponentWithPureRenderMixin], - - getInitialState: function() { - return initialSettings; - }, - - render: function() { - renderCalls++; - return
; - }, - }); - - var instance = ReactTestUtils.renderIntoDocument(); - expect(renderCalls).toBe(1); - - // Do not re-render if state is equal - var settings = { - foo: initialSettings.foo, - bar: initialSettings.bar, - }; - instance.setState(settings); - expect(renderCalls).toBe(1); - - // Re-render because one field changed - initialSettings.foo = [1, 2, 3]; - instance.setState(initialSettings); - expect(renderCalls).toBe(2); - - // Re-render because the object changed - instance.setState(getInitialState()); - expect(renderCalls).toBe(3); - }); - -}); diff --git a/src/addons/link/__tests__/LinkedStateMixin-test.js b/src/addons/link/__tests__/LinkedStateMixin-test.js deleted file mode 100644 index 1d6fa18aff85f..0000000000000 --- a/src/addons/link/__tests__/LinkedStateMixin-test.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2013-present, 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'; - - -describe('LinkedStateMixin', () => { - var LinkedStateMixin; - var React; - var ReactTestUtils; - - beforeEach(() => { - LinkedStateMixin = require('LinkedStateMixin'); - React = require('React'); - ReactTestUtils = require('ReactTestUtils'); - }); - - it('should create a ReactLink for state', () => { - var Component = React.createClass({ - mixins: [LinkedStateMixin], - - getInitialState: function() { - return {value: 'initial value'}; - }, - - render: function() { - return value is {this.state.value}; - }, - }); - var component = ReactTestUtils.renderIntoDocument(); - var link = component.linkState('value'); - expect(component.state.value).toBe('initial value'); - expect(link.value).toBe('initial value'); - link.requestChange('new value'); - expect(component.state.value).toBe('new value'); - expect(component.linkState('value').value).toBe('new value'); - }); -}); diff --git a/src/addons/link/__tests__/ReactLinkPropTypes-test.js b/src/addons/link/__tests__/ReactLinkPropTypes-test.js deleted file mode 100644 index 71406cf77d11b..0000000000000 --- a/src/addons/link/__tests__/ReactLinkPropTypes-test.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2013-present, 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 emptyFunction = require('emptyFunction'); -var LinkPropTypes = require('ReactLink').PropTypes; -var React = require('React'); -var ReactPropTypesSecret = require('ReactPropTypesSecret'); - -var invalidMessage = 'Invalid prop `testProp` supplied to `testComponent`.'; -var requiredMessage = 'The prop `testProp` is marked as required in ' + - '`testComponent`, but its value is `undefined`.'; - -function typeCheckFail(declaration, value, message) { - var props = {testProp: value}; - var error = declaration( - props, - 'testProp', - 'testComponent', - 'prop', - null, - ReactPropTypesSecret - ); - expect(error instanceof Error).toBe(true); - expect(error.message).toBe(message); -} - -function typeCheckPass(declaration, value) { - var props = {testProp: value}; - var error = declaration( - props, - 'testProp', - 'testComponent', - 'prop', - null, - ReactPropTypesSecret - ); - expect(error).toBe(null); -} - -describe('ReactLink', () => { - it('should fail if the argument does not implement the Link API', () => { - typeCheckFail( - LinkPropTypes.link(React.PropTypes.any), - {}, - 'The prop `testProp.value` is marked as required in `testComponent`, ' + - 'but its value is `undefined`.' - ); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.any), - {value: 123}, - 'The prop `testProp.requestChange` is marked as required in ' + - '`testComponent`, but its value is `undefined`.' - ); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.any), - {requestChange: emptyFunction}, - 'The prop `testProp.value` is marked as required in `testComponent`, ' + - 'but its value is `undefined`.' - ); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.any), - {value: null, requestChange: null}, - 'The prop `testProp.value` is marked as required in `testComponent`, ' + - 'but its value is `null`.' - ); - }); - - it('should allow valid links even if no type was specified', () => { - typeCheckPass( - LinkPropTypes.link(), - {value: 42, requestChange: emptyFunction} - ); - typeCheckPass( - LinkPropTypes.link(), - {value: {}, requestChange: emptyFunction} - ); - }); - - it('should allow no link to be passed at all', () => { - typeCheckPass( - LinkPropTypes.link(React.PropTypes.string), - undefined - ); - }); - - it('should allow valid links with correct value format', () => { - typeCheckPass( - LinkPropTypes.link(React.PropTypes.any), - {value: 42, requestChange: emptyFunction} - ); - typeCheckPass( - LinkPropTypes.link(React.PropTypes.number), - {value: 42, requestChange: emptyFunction} - ); - typeCheckPass( - LinkPropTypes.link(React.PropTypes.node), - {value: 42, requestChange: emptyFunction} - ); - }); - - it('should fail if the link`s value type does not match', () => { - typeCheckFail( - LinkPropTypes.link(React.PropTypes.string), - {value: 123, requestChange: emptyFunction}, - 'Invalid prop `testProp.value` of type `number` supplied to `testComponent`,' + - ' expected `string`.' - ); - }); - - it('should be implicitly optional and not warn without values', () => { - typeCheckPass(LinkPropTypes.link(), null); - typeCheckPass(LinkPropTypes.link(), undefined); - typeCheckPass(LinkPropTypes.link(React.PropTypes.string), null); - typeCheckPass(LinkPropTypes.link(React.PropTypes.string), undefined); - }); - - it('should warn for missing required values', () => { - var specifiedButIsNullMsg = 'The prop `testProp` is marked as required ' + - 'in `testComponent`, but its value is `null`.'; - typeCheckFail(LinkPropTypes.link().isRequired, null, specifiedButIsNullMsg); - typeCheckFail(LinkPropTypes.link().isRequired, undefined, requiredMessage); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.string).isRequired, - null, - specifiedButIsNullMsg - ); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.string).isRequired, - undefined, - requiredMessage - ); - }); - - it('should be compatible with React.PropTypes.oneOfType', () => { - typeCheckPass( - React.PropTypes.oneOfType([LinkPropTypes.link(React.PropTypes.number)]), - {value: 123, requestChange: emptyFunction} - ); - typeCheckFail( - React.PropTypes.oneOfType([LinkPropTypes.link(React.PropTypes.number)]), - 123, - invalidMessage - ); - typeCheckPass( - LinkPropTypes.link(React.PropTypes.oneOfType([React.PropTypes.number])), - {value: 123, requestChange: emptyFunction} - ); - typeCheckFail( - LinkPropTypes.link(React.PropTypes.oneOfType([React.PropTypes.number])), - {value: 'imastring', requestChange: emptyFunction}, - 'Invalid prop `testProp.value` supplied to `testComponent`.' - ); - }); -}); diff --git a/src/addons/transitions/__tests__/ReactCSSTransitionGroup-test.js b/src/addons/transitions/__tests__/ReactCSSTransitionGroup-test.js deleted file mode 100644 index 492b41e2e79a6..0000000000000 --- a/src/addons/transitions/__tests__/ReactCSSTransitionGroup-test.js +++ /dev/null @@ -1,330 +0,0 @@ -/** - * Copyright 2013-present, 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 CSSCore = require('CSSCore'); - -var React; -var ReactDOM; -var ReactCSSTransitionGroup; - -// Most of the real functionality is covered in other unit tests, this just -// makes sure we're wired up correctly. -describe('ReactCSSTransitionGroup', () => { - var container; - - beforeEach(() => { - jest.resetModuleRegistry(); - React = require('React'); - ReactDOM = require('ReactDOM'); - ReactCSSTransitionGroup = require('ReactCSSTransitionGroup'); - - container = document.createElement('div'); - spyOn(console, 'error'); - }); - - it('should warn if timeouts aren\'t specified', () => { - ReactDOM.render( - - - , - container - ); - - // Warning about the missing transitionLeaveTimeout prop - expect(console.error.calls.count()).toBe(1); - }); - - it('should not warn if timeouts is zero', () => { - ReactDOM.render( - - - , - container - ); - - expect(console.error.calls.count()).toBe(0); - }); - - it('should clean-up silently after the timeout elapses', () => { - var a = ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - - setTimeout.mock.calls.length = 0; - - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(2); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('two'); - expect(ReactDOM.findDOMNode(a).childNodes[1].id).toBe('one'); - - // For some reason jst is adding extra setTimeout()s and grunt test isn't, - // so we need to do this disgusting hack. - for (var i = 0; i < setTimeout.mock.calls.length; i++) { - if (setTimeout.mock.calls[i][1] === 200) { - setTimeout.mock.calls[i][0](); - break; - } - } - - // No warnings - expect(console.error.calls.count()).toBe(0); - - // The leaving child has been removed - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('two'); - }); - - it('should keep both sets of DOM nodes around', () => { - var a = ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(2); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('two'); - expect(ReactDOM.findDOMNode(a).childNodes[1].id).toBe('one'); - }); - - it('should switch transitionLeave from false to true', () => { - var a = ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(2); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('three'); - expect(ReactDOM.findDOMNode(a).childNodes[1].id).toBe('two'); - }); - - it('should work with no children', () => { - ReactDOM.render( - , - container - ); - }); - - it('should work with a null child', () => { - ReactDOM.render( - - {[null]} - , - container - ); - }); - - it('should transition from one to null', () => { - var a = ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - ReactDOM.render( - - {null} - , - container - ); - // (Here, we expect the original child to stick around but test that no - // exception is thrown) - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('one'); - }); - - it('should transition from false to one', () => { - var a = ReactDOM.render( - - {false} - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(0); - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - expect(ReactDOM.findDOMNode(a).childNodes[0].id).toBe('one'); - }); - - it('should use transition-type specific names when they\'re provided', () => { - var customTransitionNames = { - enter: 'custom-entering', - leave: 'custom-leaving', - }; - - var a = ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(1); - - // Add an element - ReactDOM.render( - - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(2); - - var enteringNode = ReactDOM.findDOMNode(a).childNodes[1]; - expect(CSSCore.hasClass(enteringNode, 'custom-entering')).toBe(true); - - // Remove an element - ReactDOM.render( - - - , - container - ); - expect(ReactDOM.findDOMNode(a).childNodes.length).toBe(2); - - var leavingNode = ReactDOM.findDOMNode(a).childNodes[0]; - expect(CSSCore.hasClass(leavingNode, 'custom-leaving')).toBe(true); - }); - - it('should clear transition timeouts when unmounted', () => { - class Component extends React.Component { - render() { - return ( - - {this.props.children} - - ); - } - } - - ReactDOM.render(, container); - ReactDOM.render(, container); - - ReactDOM.unmountComponentAtNode(container); - - // Testing that no exception is thrown here, as the timeout has been cleared. - jest.runAllTimers(); - }); - - it('should handle unmounted elements properly', () => { - class Child extends React.Component { - render() { - if (!this.props.show) { - return null; - } - return
; - } - } - - class Component extends React.Component { - state = { showChild: true }; - - componentDidMount() { - this.setState({ showChild: false }); - } - - render() { - return ( - - - - ); - } - } - - ReactDOM.render(, container); - - // Testing that no exception is thrown here, as the timeout has been cleared. - jest.runAllTimers(); - }); -}); diff --git a/src/addons/transitions/__tests__/ReactTransitionGroup-test.js b/src/addons/transitions/__tests__/ReactTransitionGroup-test.js deleted file mode 100644 index 25e6dfdcd1f01..0000000000000 --- a/src/addons/transitions/__tests__/ReactTransitionGroup-test.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright 2013-present, 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 ReactDOM; -var ReactTransitionGroup; - -// Most of the real functionality is covered in other unit tests, this just -// makes sure we're wired up correctly. -describe('ReactTransitionGroup', () => { - var container; - - beforeEach(() => { - React = require('React'); - ReactDOM = require('ReactDOM'); - ReactTransitionGroup = require('ReactTransitionGroup'); - - container = document.createElement('div'); - }); - - - it('should handle willEnter correctly', () => { - var log = []; - - class Child extends React.Component { - componentDidMount() { - log.push('didMount'); - } - - componentWillAppear = (cb) => { - log.push('willAppear'); - cb(); - }; - - componentDidAppear = () => { - log.push('didAppear'); - }; - - componentWillEnter = (cb) => { - log.push('willEnter'); - cb(); - }; - - componentDidEnter = () => { - log.push('didEnter'); - }; - - componentWillLeave = (cb) => { - log.push('willLeave'); - cb(); - }; - - componentDidLeave = () => { - log.push('didLeave'); - }; - - componentWillUnmount() { - log.push('willUnmount'); - } - - render() { - return ; - } - } - - class Component extends React.Component { - state = {count: 1}; - - render() { - var children = []; - for (var i = 0; i < this.state.count; i++) { - children.push(); - } - return {children}; - } - } - - var instance = ReactDOM.render(, container); - expect(log).toEqual(['didMount', 'willAppear', 'didAppear']); - - log = []; - instance.setState({count: 2}, function() { - expect(log).toEqual(['didMount', 'willEnter', 'didEnter']); - - log = []; - instance.setState({count: 1}, function() { - expect(log).toEqual(['willLeave', 'didLeave', 'willUnmount']); - }); - }); - }); - - it('should handle enter/leave/enter/leave correctly', () => { - var log = []; - var willEnterCb; - - class Child extends React.Component { - componentDidMount() { - log.push('didMount'); - } - - componentWillEnter = (cb) => { - log.push('willEnter'); - willEnterCb = cb; - }; - - componentDidEnter = () => { - log.push('didEnter'); - }; - - componentWillLeave = (cb) => { - log.push('willLeave'); - cb(); - }; - - componentDidLeave = () => { - log.push('didLeave'); - }; - - componentWillUnmount() { - log.push('willUnmount'); - } - - render() { - return ; - } - } - - class Component extends React.Component { - state = {count: 1}; - - render() { - var children = []; - for (var i = 0; i < this.state.count; i++) { - children.push(); - } - return {children}; - } - } - - var instance = ReactDOM.render(, container); - expect(log).toEqual(['didMount']); - instance.setState({count: 2}); - expect(log).toEqual(['didMount', 'didMount', 'willEnter']); - for (var k = 0; k < 5; k++) { - instance.setState({count: 2}); - expect(log).toEqual(['didMount', 'didMount', 'willEnter']); - instance.setState({count: 1}); - } - // other animations are blocked until willEnterCb is called - willEnterCb(); - expect(log).toEqual([ - 'didMount', 'didMount', 'willEnter', - 'didEnter', 'willLeave', 'didLeave', 'willUnmount', - ]); - }); - - it('should handle enter/leave/enter correctly', () => { - var log = []; - var willEnterCb; - - class Child extends React.Component { - componentDidMount() { - log.push('didMount'); - } - - componentWillEnter = (cb) => { - log.push('willEnter'); - willEnterCb = cb; - }; - - componentDidEnter = () => { - log.push('didEnter'); - }; - - componentWillLeave = (cb) => { - log.push('willLeave'); - cb(); - }; - - componentDidLeave = () => { - log.push('didLeave'); - }; - - componentWillUnmount() { - log.push('willUnmount'); - } - - render() { - return ; - } - } - - class Component extends React.Component { - state = {count: 1}; - - render() { - var children = []; - for (var i = 0; i < this.state.count; i++) { - children.push(); - } - return {children}; - } - } - - var instance = ReactDOM.render(, container); - expect(log).toEqual(['didMount']); - instance.setState({count: 2}); - expect(log).toEqual(['didMount', 'didMount', 'willEnter']); - for (var k = 0; k < 5; k++) { - instance.setState({count: 1}); - expect(log).toEqual(['didMount', 'didMount', 'willEnter']); - instance.setState({count: 2}); - } - willEnterCb(); - expect(log).toEqual([ - 'didMount', 'didMount', 'willEnter', 'didEnter', - ]); - }); - - it('should handle entering/leaving several elements at once', () => { - var log = []; - - class Child extends React.Component { - componentDidMount() { - log.push('didMount' + this.props.id); - } - - componentWillEnter = (cb) => { - log.push('willEnter' + this.props.id); - cb(); - }; - - componentDidEnter = () => { - log.push('didEnter' + this.props.id); - }; - - componentWillLeave = (cb) => { - log.push('willLeave' + this.props.id); - cb(); - }; - - componentDidLeave = () => { - log.push('didLeave' + this.props.id); - }; - - componentWillUnmount() { - log.push('willUnmount' + this.props.id); - } - - render() { - return ; - } - } - - class Component extends React.Component { - state = {count: 1}; - - render() { - var children = []; - for (var i = 0; i < this.state.count; i++) { - children.push(); - } - return {children}; - } - } - - var instance = ReactDOM.render(, container); - expect(log).toEqual(['didMount0']); - log = []; - - instance.setState({count: 3}); - expect(log).toEqual([ - 'didMount1', 'didMount2', 'willEnter1', 'didEnter1', - 'willEnter2', 'didEnter2', - ]); - log = []; - - instance.setState({count: 0}); - expect(log).toEqual([ - 'willLeave0', 'didLeave0', 'willLeave1', 'didLeave1', - 'willLeave2', 'didLeave2', 'willUnmount0', 'willUnmount1', 'willUnmount2', - ]); - }); - - it('should warn for duplicated keys', () => { - spyOn(console, 'error'); - - class Component extends React.Component { - render() { - var children = [
,
]; - return {children}; - } - } - - ReactDOM.render(, container); - - expect(console.error.calls.count()).toBe(2); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: flattenChildren(...): ' + - 'Encountered two children with the same key, `1`. ' + - 'Child keys must be unique; when two children share a key, ' + - 'only the first child will be used.' - ); - expect(console.error.calls.argsFor(1)[0]).toBe( - 'Warning: flattenChildren(...): ' + - 'Encountered two children with the same key, `1`. ' + - 'Child keys must be unique; when two children share a key, ' + - 'only the first child will be used.' - ); - }); -}); diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js index bda23752f7128..a02b9882252cf 100644 --- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js +++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js @@ -41,33 +41,31 @@ describe('ReactContextValidator', () => { // ensure that this is not required for ES6 classes with Flow. it('should filter out context not in contextTypes', () => { - var Component = React.createClass({ - contextTypes: { - foo: React.PropTypes.string, - }, - - render: function() { + class Component extends React.Component { + render() { return
; - }, - }); - - var ComponentInFooBarContext = React.createClass({ - childContextTypes: { - foo: React.PropTypes.string, - bar: React.PropTypes.number, - }, - - getChildContext: function() { + } + } + Component.contextTypes = { + foo: React.PropTypes.string, + }; + + class ComponentInFooBarContext extends React.Component { + getChildContext() { return { foo: 'abc', bar: 123, }; - }, + } - render: function() { + render() { return ; - }, - }); + } + } + ComponentInFooBarContext.childContextTypes = { + foo: React.PropTypes.string, + bar: React.PropTypes.number, + }; var instance = ReactTestUtils.renderIntoDocument(); reactComponentExpect(instance).expectRenderedChild().scalarContextEqual({foo: 'abc'}); @@ -79,51 +77,50 @@ describe('ReactContextValidator', () => { var actualComponentWillUpdate; var actualComponentDidUpdate; - var Parent = React.createClass({ - childContextTypes: { - foo: React.PropTypes.string.isRequired, - bar: React.PropTypes.string.isRequired, - }, - - getChildContext: function() { + class Parent extends React.Component { + getChildContext() { return { foo: this.props.foo, bar: 'bar', }; - }, + } - render: function() { + render() { return ; - }, - }); - - var Component = React.createClass({ - contextTypes: { - foo: React.PropTypes.string, - }, - - componentWillReceiveProps: function(nextProps, nextContext) { + } + } + Parent.childContextTypes = { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.string.isRequired, + }; + + class Component extends React.Component { + componentWillReceiveProps(nextProps, nextContext) { actualComponentWillReceiveProps = nextContext; return true; - }, + } - shouldComponentUpdate: function(nextProps, nextState, nextContext) { + shouldComponentUpdate(nextProps, nextState, nextContext) { actualShouldComponentUpdate = nextContext; return true; - }, + } - componentWillUpdate: function(nextProps, nextState, nextContext) { + componentWillUpdate(nextProps, nextState, nextContext) { actualComponentWillUpdate = nextContext; - }, + } - componentDidUpdate: function(prevProps, prevState, prevContext) { + componentDidUpdate(prevProps, prevState, prevContext) { actualComponentDidUpdate = prevContext; - }, + } - render: function() { + render() { return
; - }, - }); + } + } + Component.contextTypes = { + foo: React.PropTypes.string, + }; + var container = document.createElement('div'); ReactDOM.render(, container); @@ -137,15 +134,14 @@ describe('ReactContextValidator', () => { it('should check context types', () => { spyOn(console, 'error'); - var Component = React.createClass({ - contextTypes: { - foo: React.PropTypes.string.isRequired, - }, - - render: function() { + class Component extends React.Component { + render() { return
; - }, - }); + } + } + Component.contextTypes = { + foo: React.PropTypes.string.isRequired, + }; ReactTestUtils.renderIntoDocument(); @@ -157,21 +153,20 @@ describe('ReactContextValidator', () => { ' in Component (at **)' ); - var ComponentInFooStringContext = React.createClass({ - childContextTypes: { - foo: React.PropTypes.string, - }, - - getChildContext: function() { + class ComponentInFooStringContext extends React.Component { + getChildContext() { return { foo: this.props.fooValue, }; - }, + } - render: function() { + render() { return ; - }, - }); + } + } + ComponentInFooStringContext.childContextTypes = { + foo: React.PropTypes.string, + }; ReactTestUtils.renderIntoDocument( @@ -180,21 +175,20 @@ describe('ReactContextValidator', () => { // Previous call should not error expect(console.error.calls.count()).toBe(1); - var ComponentInFooNumberContext = React.createClass({ - childContextTypes: { - foo: React.PropTypes.number, - }, - - getChildContext: function() { + class ComponentInFooNumberContext extends React.Component { + getChildContext() { return { foo: this.props.fooValue, }; - }, + } - render: function() { + render() { return ; - }, - }); + } + } + ComponentInFooNumberContext.childContextTypes = { + foo: React.PropTypes.number, + }; ReactTestUtils.renderIntoDocument(); @@ -211,20 +205,19 @@ describe('ReactContextValidator', () => { it('should check child context types', () => { spyOn(console, 'error'); - var Component = React.createClass({ - childContextTypes: { - foo: React.PropTypes.string.isRequired, - bar: React.PropTypes.number, - }, - - getChildContext: function() { + class Component extends React.Component { + getChildContext() { return this.props.testContext; - }, + } - render: function() { + render() { return
; - }, - }); + } + } + Component.childContextTypes = { + foo: React.PropTypes.string.isRequired, + bar: React.PropTypes.number, + }; ReactTestUtils.renderIntoDocument(); expect(console.error.calls.count()).toBe(1); diff --git a/src/isomorphic/classic/class/ReactClass.js b/src/isomorphic/classic/class/ReactClass.js index a387299fcc536..7bc995f5506d3 100644 --- a/src/isomorphic/classic/class/ReactClass.js +++ b/src/isomorphic/classic/class/ReactClass.js @@ -13,877 +13,32 @@ var ReactComponent = require('ReactComponent'); var ReactElement = require('ReactElement'); -var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue'); -var emptyObject = require('emptyObject'); -var invariant = require('invariant'); var warning = require('warning'); -import type { ReactPropTypeLocations } from 'ReactPropTypeLocations'; +var factory = require('react-create-class/factory'); -var MIXINS_KEY = 'mixins'; - -// Helper function to allow the creation of anonymous functions which do not -// have .name set to the name of the variable being assigned to. -function identity(fn) { - return fn; -} - -/** - * Policies that describe methods in `ReactClassInterface`. - */ -type SpecPolicy = - /** - * These methods may be defined only once by the class specification or mixin. - */ - 'DEFINE_ONCE' | - /** - * These methods may be defined by both the class specification and mixins. - * Subsequent definitions will be chained. These methods must return void. - */ - 'DEFINE_MANY' | - /** - * These methods are overriding the base class. - */ - 'OVERRIDE_BASE' | - /** - * These methods are similar to DEFINE_MANY, except we assume they return - * objects. We try to merge the keys of the return values of all the mixed in - * functions. If there is a key conflict we throw. - */ - 'DEFINE_MANY_MERGED'; - - -var injectedMixins = []; - -/** - * Composite components are higher-level components that compose other composite - * or host components. - * - * To create a new type of `ReactClass`, pass a specification of - * your new class to `React.createClass`. The only requirement of your class - * specification is that you implement a `render` method. - * - * var MyComponent = React.createClass({ - * render: function() { - * return
Hello World
; - * } - * }); - * - * The class specification supports a specific protocol of methods that have - * special meaning (e.g. `render`). See `ReactClassInterface` for - * more the comprehensive protocol. Any other properties and methods in the - * class specification will be available on the prototype. - * - * @interface ReactClassInterface - * @internal - */ -var ReactClassInterface: {[key: string]: SpecPolicy} = { - - /** - * An array of Mixin objects to include when defining your component. - * - * @type {array} - * @optional - */ - mixins: 'DEFINE_MANY', - - /** - * An object containing properties and methods that should be defined on - * the component's constructor instead of its prototype (static methods). - * - * @type {object} - * @optional - */ - statics: 'DEFINE_MANY', - - /** - * Definition of prop types for this component. - * - * @type {object} - * @optional - */ - propTypes: 'DEFINE_MANY', - - /** - * Definition of context types for this component. - * - * @type {object} - * @optional - */ - contextTypes: 'DEFINE_MANY', - - /** - * Definition of context types this component sets for its children. - * - * @type {object} - * @optional - */ - childContextTypes: 'DEFINE_MANY', - - // ==== Definition methods ==== - - /** - * Invoked when the component is mounted. Values in the mapping will be set on - * `this.props` if that prop is not specified (i.e. using an `in` check). - * - * This method is invoked before `getInitialState` and therefore cannot rely - * on `this.state` or use `this.setState`. - * - * @return {object} - * @optional - */ - getDefaultProps: 'DEFINE_MANY_MERGED', - - /** - * Invoked once before the component is mounted. The return value will be used - * as the initial value of `this.state`. - * - * getInitialState: function() { - * return { - * isOn: false, - * fooBaz: new BazFoo() - * } - * } - * - * @return {object} - * @optional - */ - getInitialState: 'DEFINE_MANY_MERGED', - - /** - * @return {object} - * @optional - */ - getChildContext: 'DEFINE_MANY_MERGED', - - /** - * Uses props from `this.props` and state from `this.state` to render the - * structure of the component. - * - * No guarantees are made about when or how often this method is invoked, so - * it must not have side effects. - * - * render: function() { - * var name = this.props.name; - * return
Hello, {name}!
; - * } - * - * @return {ReactComponent} - * @nosideeffects - * @required - */ - render: 'DEFINE_ONCE', - - - - // ==== Delegate methods ==== - - /** - * Invoked when the component is initially created and about to be mounted. - * This may have side effects, but any external subscriptions or data created - * by this method must be cleaned up in `componentWillUnmount`. - * - * @optional - */ - componentWillMount: 'DEFINE_MANY', - - /** - * Invoked when the component has been mounted and has a DOM representation. - * However, there is no guarantee that the DOM node is in the document. - * - * Use this as an opportunity to operate on the DOM when the component has - * been mounted (initialized and rendered) for the first time. - * - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidMount: 'DEFINE_MANY', - - /** - * Invoked before the component receives new props. - * - * Use this as an opportunity to react to a prop transition by updating the - * state using `this.setState`. Current props are accessed via `this.props`. - * - * componentWillReceiveProps: function(nextProps, nextContext) { - * this.setState({ - * likesIncreasing: nextProps.likeCount > this.props.likeCount - * }); - * } - * - * NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop - * transition may cause a state change, but the opposite is not true. If you - * need it, you are probably looking for `componentWillUpdate`. - * - * @param {object} nextProps - * @optional - */ - componentWillReceiveProps: 'DEFINE_MANY', - - /** - * Invoked while deciding if the component should be updated as a result of - * receiving new props, state and/or context. - * - * Use this as an opportunity to `return false` when you're certain that the - * transition to the new props/state/context will not require a component - * update. - * - * shouldComponentUpdate: function(nextProps, nextState, nextContext) { - * return !equal(nextProps, this.props) || - * !equal(nextState, this.state) || - * !equal(nextContext, this.context); - * } - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @return {boolean} True if the component should update. - * @optional - */ - shouldComponentUpdate: 'DEFINE_ONCE', - - /** - * Invoked when the component is about to update due to a transition from - * `this.props`, `this.state` and `this.context` to `nextProps`, `nextState` - * and `nextContext`. - * - * Use this as an opportunity to perform preparation before an update occurs. - * - * NOTE: You **cannot** use `this.setState()` in this method. - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @param {ReactReconcileTransaction} transaction - * @optional - */ - componentWillUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component's DOM representation has been updated. - * - * Use this as an opportunity to operate on the DOM when the component has - * been updated. - * - * @param {object} prevProps - * @param {?object} prevState - * @param {?object} prevContext - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component is about to be removed from its parent and have - * its DOM representation destroyed. - * - * Use this as an opportunity to deallocate any external resources. - * - * NOTE: There is no `componentDidUnmount` since your component will have been - * destroyed by that point. - * - * @optional - */ - componentWillUnmount: 'DEFINE_MANY', - - - - // ==== Advanced methods ==== - - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @internal - * @overridable - */ - updateComponent: 'OVERRIDE_BASE', - -}; - -/** - * Mapping from class specification keys to special processing functions. - * - * Although these are declared like instance properties in the specification - * when defining classes using `React.createClass`, they are actually static - * and are accessible on the constructor instead of the prototype. Despite - * being static, they must be defined outside of the "statics" key under - * which all other static methods are defined. - */ -var RESERVED_SPEC_KEYS = { - displayName: function(Constructor, displayName) { - Constructor.displayName = displayName; - }, - mixins: function(Constructor, mixins) { - if (mixins) { - for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(Constructor, mixins[i]); - } - } - }, - childContextTypes: function(Constructor, childContextTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - childContextTypes, - 'childContext' - ); - } - Constructor.childContextTypes = Object.assign( - {}, - Constructor.childContextTypes, - childContextTypes - ); - }, - contextTypes: function(Constructor, contextTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - contextTypes, - 'context' - ); - } - Constructor.contextTypes = Object.assign( - {}, - Constructor.contextTypes, - contextTypes - ); - }, - /** - * Special case getDefaultProps which should move into statics but requires - * automatic merging. - */ - getDefaultProps: function(Constructor, getDefaultProps) { - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps = createMergedResultFunction( - Constructor.getDefaultProps, - getDefaultProps - ); - } else { - Constructor.getDefaultProps = getDefaultProps; - } - }, - propTypes: function(Constructor, propTypes) { - if (__DEV__) { - validateTypeDef( - Constructor, - propTypes, - 'prop' - ); - } - Constructor.propTypes = Object.assign( - {}, - Constructor.propTypes, - propTypes - ); - }, - statics: function(Constructor, statics) { - mixStaticSpecIntoComponent(Constructor, statics); - }, - autobind: function() {}, // noop -}; - -function validateTypeDef( - Constructor, - typeDef, - location: ReactPropTypeLocations, -) { - for (var propName in typeDef) { - if (typeDef.hasOwnProperty(propName)) { - // use a warning instead of an invariant so components - // don't show up in prod but only in __DEV__ - warning( - typeof typeDef[propName] === 'function', - '%s: %s type `%s` is invalid; it must be a function, usually from ' + - 'React.PropTypes.', - Constructor.displayName || 'ReactClass', - ReactPropTypeLocationNames[location], - propName - ); - } - } -} - -function validateMethodOverride(isAlreadyDefined, name) { - var specPolicy = ReactClassInterface.hasOwnProperty(name) ? - ReactClassInterface[name] : - null; - - // Disallow overriding of base class methods unless explicitly allowed. - if (ReactClassMixin.hasOwnProperty(name)) { - invariant( - specPolicy === 'OVERRIDE_BASE', - 'ReactClassInterface: You are attempting to override ' + - '`%s` from your class specification. Ensure that your method names ' + - 'do not overlap with React methods.', - name - ); - } - - // Disallow defining methods more than once unless explicitly allowed. - if (isAlreadyDefined) { - invariant( - specPolicy === 'DEFINE_MANY' || - specPolicy === 'DEFINE_MANY_MERGED', - 'ReactClassInterface: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be due ' + - 'to a mixin.', - name - ); - } -} - -/** - * Mixin helper which handles policy validation and reserved - * specification keys when building React classes. - */ -function mixSpecIntoComponent(Constructor, spec) { - if (!spec) { - if (__DEV__) { - var typeofSpec = typeof spec; - var isMixinValid = typeofSpec === 'object' && spec !== null; - - warning( - isMixinValid, - '%s: You\'re attempting to include a mixin that is either null ' + - 'or not an object. Check the mixins included by the component, ' + - 'as well as any mixins they include themselves. ' + - 'Expected object but got %s.', - Constructor.displayName || 'ReactClass', - spec === null ? null : typeofSpec - ); - } - - return; - } - - invariant( - typeof spec !== 'function', - 'ReactClass: You\'re attempting to ' + - 'use a component class or function as a mixin. Instead, just use a ' + - 'regular object.' - ); - invariant( - !ReactElement.isValidElement(spec), - 'ReactClass: You\'re attempting to ' + - 'use a component as a mixin. Instead, just use a regular object.' - ); - - var proto = Constructor.prototype; - var autoBindPairs = proto.__reactAutoBindPairs; - - // By handling mixins before any other properties, we ensure the same - // chaining order is applied to methods with DEFINE_MANY policy, whether - // mixins are listed before or after these methods in the spec. - if (spec.hasOwnProperty(MIXINS_KEY)) { - RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins); - } - - for (var name in spec) { - if (!spec.hasOwnProperty(name)) { - continue; - } - - if (name === MIXINS_KEY) { - // We have already handled mixins in a special case above. - continue; - } - - var property = spec[name]; - var isAlreadyDefined = proto.hasOwnProperty(name); - validateMethodOverride(isAlreadyDefined, name); - - if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](Constructor, property); - } else { - // Setup methods on prototype: - // The following member methods should not be automatically bound: - // 1. Expected ReactClass methods (in the "interface"). - // 2. Overridden methods (that were mixed in). - var isReactClassMethod = - ReactClassInterface.hasOwnProperty(name); - var isFunction = typeof property === 'function'; - var shouldAutoBind = - isFunction && - !isReactClassMethod && - !isAlreadyDefined && - spec.autobind !== false; - - if (shouldAutoBind) { - autoBindPairs.push(name, property); - proto[name] = property; - } else { - if (isAlreadyDefined) { - var specPolicy = ReactClassInterface[name]; - - // These cases should already be caught by validateMethodOverride. - invariant( - isReactClassMethod && ( - specPolicy === 'DEFINE_MANY_MERGED' || - specPolicy === 'DEFINE_MANY' - ), - 'ReactClass: Unexpected spec policy %s for key %s ' + - 'when mixing in component specs.', - specPolicy, - name - ); - - // For methods which are defined more than once, call the existing - // methods before calling the new property, merging if appropriate. - if (specPolicy === 'DEFINE_MANY_MERGED') { - proto[name] = createMergedResultFunction(proto[name], property); - } else if (specPolicy === 'DEFINE_MANY') { - proto[name] = createChainedFunction(proto[name], property); - } - } else { - proto[name] = property; - if (__DEV__) { - // Add verbose displayName to the function, which helps when looking - // at profiling tools. - if (typeof property === 'function' && spec.displayName) { - proto[name].displayName = spec.displayName + '_' + name; - } - } - } - } - } - } -} - -function mixStaticSpecIntoComponent(Constructor, statics) { - if (!statics) { - return; - } - for (var name in statics) { - var property = statics[name]; - if (!statics.hasOwnProperty(name)) { - continue; - } - - var isReserved = name in RESERVED_SPEC_KEYS; - invariant( - !isReserved, - 'ReactClass: You are attempting to define a reserved ' + - 'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' + - 'as an instance property instead; it will still be accessible on the ' + - 'constructor.', - name - ); - - var isInherited = name in Constructor; - invariant( - !isInherited, - 'ReactClass: You are attempting to define ' + - '`%s` on your component more than once. This conflict may be ' + - 'due to a mixin.', - name - ); - Constructor[name] = property; - } -} - -/** - * Merge two objects, but throw if both contain the same key. - * - * @param {object} one The first object, which is mutated. - * @param {object} two The second object - * @return {object} one after it has been mutated to contain everything in two. - */ -function mergeIntoWithNoDuplicateKeys(one, two) { - invariant( - one && two && typeof one === 'object' && typeof two === 'object', - 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.' - ); - - for (var key in two) { - if (two.hasOwnProperty(key)) { - invariant( - one[key] === undefined, - 'mergeIntoWithNoDuplicateKeys(): ' + - 'Tried to merge two objects with the same key: `%s`. This conflict ' + - 'may be due to a mixin; in particular, this may be caused by two ' + - 'getInitialState() or getDefaultProps() methods returning objects ' + - 'with clashing keys.', - key - ); - one[key] = two[key]; - } - } - return one; -} - -/** - * Creates a function that invokes two functions and merges their return values. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createMergedResultFunction(one, two) { - return function mergedResult() { - var a = one.apply(this, arguments); - var b = two.apply(this, arguments); - if (a == null) { - return b; - } else if (b == null) { - return a; - } - var c = {}; - mergeIntoWithNoDuplicateKeys(c, a); - mergeIntoWithNoDuplicateKeys(c, b); - return c; - }; -} - -/** - * Creates a function that invokes two functions and ignores their return vales. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ -function createChainedFunction(one, two) { - return function chainedFunction() { - one.apply(this, arguments); - two.apply(this, arguments); - }; -} - -/** - * Binds a method to the component. - * - * @param {object} component Component whose method is going to be bound. - * @param {function} method Method to be bound. - * @return {function} The bound method. - */ -function bindAutoBindMethod(component, method) { - var boundMethod = method.bind(component); - if (__DEV__) { - boundMethod.__reactBoundContext = component; - boundMethod.__reactBoundMethod = method; - boundMethod.__reactBoundArguments = null; - var componentName = component.constructor.displayName; - var _bind = boundMethod.bind; - boundMethod.bind = function(newThis, ...args) { - // User is trying to bind() an autobound method; we effectively will - // ignore the value of "this" that the user is trying to use, so - // let's warn. - if (newThis !== component && newThis !== null) { - warning( - false, - 'bind(): React component methods may only be bound to the ' + - 'component instance. See %s', - componentName - ); - } else if (!args.length) { - warning( - false, - 'bind(): You are binding a component method to the component. ' + - 'React does this for you automatically in a high-performance ' + - 'way, so you can safely remove this call. See %s', - componentName - ); - return boundMethod; - } - var reboundMethod = _bind.apply(boundMethod, arguments); - reboundMethod.__reactBoundContext = component; - reboundMethod.__reactBoundMethod = method; - reboundMethod.__reactBoundArguments = args; - return reboundMethod; - }; - } - return boundMethod; -} - -/** - * Binds all auto-bound methods in a component. - * - * @param {object} component Component whose method is going to be bound. - */ -function bindAutoBindMethods(component) { - var pairs = component.__reactAutoBindPairs; - for (var i = 0; i < pairs.length; i += 2) { - var autoBindKey = pairs[i]; - var method = pairs[i + 1]; - component[autoBindKey] = bindAutoBindMethod( - component, - method - ); - } -} - -/** - * Add more to the ReactClass base class. These are all legacy features and - * therefore not already part of the modern ReactComponent. - */ -var ReactClassMixin = { - - /** - * TODO: This will be deprecated because state should always keep a consistent - * type signature and the only use case for this, is to avoid that. - */ - replaceState: function(newState, callback) { - this.updater.enqueueReplaceState(this, newState); - if (callback) { - this.updater.enqueueCallback(this, callback, 'replaceState'); - } - }, - - /** - * Checks whether or not this composite component is mounted. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function() { - return this.updater.isMounted(this); - }, -}; - -var ReactClassComponent = function() {}; -Object.assign( - ReactClassComponent.prototype, - ReactComponent.prototype, - ReactClassMixin +var createClass = factory( + ReactComponent, + ReactElement.isValidElement, + ReactNoopUpdateQueue ); -/** - * Module for creating composite components. - * - * @class ReactClass - */ -var ReactClass = { - - /** - * Creates a composite component class given a class specification. - * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass - * - * @param {object} spec Class specification (which must define `render`). - * @return {function} Component constructor function. - * @public - */ - createClass: function(spec) { - // To keep our warnings more understandable, we'll use a little hack here to - // ensure that Constructor.name !== 'Constructor'. This makes sure we don't - // unnecessarily identify a class without displayName as 'Constructor'. - var Constructor = identity(function(props, context, updater) { - // This constructor gets overridden by mocks. The argument is used - // by mocks to assert on what gets mounted. - - if (__DEV__) { - warning( - this instanceof Constructor, - 'Something is calling a React component directly. Use a factory or ' + - 'JSX instead. See: https://fb.me/react-legacyfactory' - ); - } - - // Wire up auto-binding - if (this.__reactAutoBindPairs.length) { - bindAutoBindMethods(this); - } - - this.props = props; - this.context = context; - this.refs = emptyObject; - this.updater = updater || ReactNoopUpdateQueue; - - this.state = null; - - // ReactClasses doesn't have constructors. Instead, they use the - // getInitialState and componentWillMount methods for initialization. - - var initialState = this.getInitialState ? this.getInitialState() : null; - if (__DEV__) { - // We allow auto-mocks to proceed as if they're returning null. - if (initialState === undefined && - this.getInitialState._isMockFunction) { - // This is probably bad practice. Consider warning here and - // deprecating this convenience. - initialState = null; - } - } - invariant( - typeof initialState === 'object' && !Array.isArray(initialState), - '%s.getInitialState(): must return an object or null', - Constructor.displayName || 'ReactCompositeComponent' - ); - - this.state = initialState; - }); - Constructor.prototype = new ReactClassComponent(); - Constructor.prototype.constructor = Constructor; - Constructor.prototype.__reactAutoBindPairs = []; - - injectedMixins.forEach( - mixSpecIntoComponent.bind(null, Constructor) - ); - - mixSpecIntoComponent(Constructor, spec); - - // Initialize the defaultProps property after all mixins have been merged. - if (Constructor.getDefaultProps) { - Constructor.defaultProps = Constructor.getDefaultProps(); - } - - if (__DEV__) { - // This is a tag to indicate that the use of these method names is ok, - // since it's used with createClass. If it's not, then it's likely a - // mistake so we'll warn you to use the static property, property - // initializer or constructor respectively. - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps.isReactClassApproved = {}; - } - if (Constructor.prototype.getInitialState) { - Constructor.prototype.getInitialState.isReactClassApproved = {}; - } - } - - invariant( - Constructor.prototype.render, - 'createClass(...): Class specification must implement a `render` method.' - ); +let didWarnDeprecated = false; +module.exports = { + createClass(...args) { if (__DEV__) { warning( - !Constructor.prototype.componentShouldUpdate, - '%s has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.', - spec.displayName || 'A component' - ); - warning( - !Constructor.prototype.componentWillRecieveProps, - '%s has a method called ' + - 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', - spec.displayName || 'A component' + didWarnDeprecated, + 'React.createClass is deprecated and will be removed in version 16. ' + + 'Use plain JavaScript classes instead. If you\'re not yet ready to ' + + 'migrate, react-create-class is available on npm as a ' + + 'drop-in replacement.' ); + didWarnDeprecated = true; } - - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactClassInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } - - return Constructor; + return createClass(...args); }, - - injection: { - injectMixin: function(mixin) { - injectedMixins.push(mixin); - }, - }, - }; - -module.exports = ReactClass; diff --git a/src/isomorphic/classic/class/__tests__/ReactBind-test.js b/src/isomorphic/classic/class/__tests__/ReactBind-test.js deleted file mode 100644 index a5b612eba3a71..0000000000000 --- a/src/isomorphic/classic/class/__tests__/ReactBind-test.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Copyright 2013-present, 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 - */ -/*global global:true*/ -'use strict'; - -var React = require('React'); -var ReactTestUtils = require('ReactTestUtils'); -var reactComponentExpect = require('reactComponentExpect'); - -// TODO: Test render and all stock methods. -describe('autobinding', () => { - - it('Holds reference to instance', () => { - - var mouseDidEnter = jest.fn(); - var mouseDidLeave = jest.fn(); - var mouseDidClick = jest.fn(); - - var TestBindComponent = React.createClass({ - getInitialState: function() { - return {something: 'hi'}; - }, - onMouseEnter: mouseDidEnter, - onMouseLeave: mouseDidLeave, - onClick: mouseDidClick, - - // auto binding only occurs on top level functions in class defs. - badIdeas: { - badBind: function() { - void this.state.something; - }, - }, - - render: function() { - return ( -
- ); - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - var instance2 = ; - var mountedInstance2 = ReactTestUtils.renderIntoDocument(instance2); - var rendered2 = reactComponentExpect(mountedInstance2) - .expectRenderedChild() - .instance(); - - expect(function() { - var badIdea = instance1.badIdeas.badBind; - badIdea(); - }).toThrow(); - - expect(mountedInstance1.onClick).not.toBe(mountedInstance2.onClick); - - ReactTestUtils.Simulate.click(rendered1); - expect(mouseDidClick.mock.instances.length).toBe(1); - expect(mouseDidClick.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.click(rendered2); - expect(mouseDidClick.mock.instances.length).toBe(2); - expect(mouseDidClick.mock.instances[1]).toBe(mountedInstance2); - - ReactTestUtils.Simulate.mouseOver(rendered1); - expect(mouseDidEnter.mock.instances.length).toBe(1); - expect(mouseDidEnter.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.mouseOver(rendered2); - expect(mouseDidEnter.mock.instances.length).toBe(2); - expect(mouseDidEnter.mock.instances[1]).toBe(mountedInstance2); - - ReactTestUtils.Simulate.mouseOut(rendered1); - expect(mouseDidLeave.mock.instances.length).toBe(1); - expect(mouseDidLeave.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.mouseOut(rendered2); - expect(mouseDidLeave.mock.instances.length).toBe(2); - expect(mouseDidLeave.mock.instances[1]).toBe(mountedInstance2); - }); - - it('works with mixins', () => { - var mouseDidClick = jest.fn(); - - var TestMixin = { - onClick: mouseDidClick, - }; - - var TestBindComponent = React.createClass({ - mixins: [TestMixin], - - render: function() { - return
; - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - ReactTestUtils.Simulate.click(rendered1); - expect(mouseDidClick.mock.instances.length).toBe(1); - expect(mouseDidClick.mock.instances[0]).toBe(mountedInstance1); - }); - - it('warns if you try to bind to this', () => { - spyOn(console, 'error'); - - var TestBindComponent = React.createClass({ - handleClick: function() { }, - render: function() { - return
; - }, - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: bind(): You are binding a component method to the component. ' + - 'React does this for you automatically in a high-performance ' + - 'way, so you can safely remove this call. See TestBindComponent' - ); - }); - - it('does not warn if you pass an auto-bound method to setState', () => { - spyOn(console, 'error'); - - var TestBindComponent = React.createClass({ - getInitialState: function() { - return {foo: 1}; - }, - componentDidMount: function() { - this.setState({foo: 2}, this.handleUpdate); - }, - handleUpdate: function() { - - }, - render: function() { - return
; - }, - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.error.calls.count()).toBe(0); - }); - -}); diff --git a/src/isomorphic/classic/class/__tests__/ReactBindOptout-test.js b/src/isomorphic/classic/class/__tests__/ReactBindOptout-test.js deleted file mode 100644 index 635b594dee276..0000000000000 --- a/src/isomorphic/classic/class/__tests__/ReactBindOptout-test.js +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright 2013-present, 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 - */ -/*global global:true*/ -'use strict'; - -var React = require('React'); -var ReactTestUtils = require('ReactTestUtils'); -var reactComponentExpect = require('reactComponentExpect'); - -// TODO: Test render and all stock methods. -describe('autobind optout', () => { - - it('should work with manual binding', () => { - - var mouseDidEnter = jest.fn(); - var mouseDidLeave = jest.fn(); - var mouseDidClick = jest.fn(); - - var TestBindComponent = React.createClass({ - autobind: false, - getInitialState: function() { - return {something: 'hi'}; - }, - onMouseEnter: mouseDidEnter, - onMouseLeave: mouseDidLeave, - onClick: mouseDidClick, - - render: function() { - return ( -
- ); - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - var instance2 = ; - var mountedInstance2 = ReactTestUtils.renderIntoDocument(instance2); - var rendered2 = reactComponentExpect(mountedInstance2) - .expectRenderedChild() - .instance(); - - ReactTestUtils.Simulate.click(rendered1); - expect(mouseDidClick.mock.instances.length).toBe(1); - expect(mouseDidClick.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.click(rendered2); - expect(mouseDidClick.mock.instances.length).toBe(2); - expect(mouseDidClick.mock.instances[1]).toBe(mountedInstance2); - - ReactTestUtils.Simulate.mouseOver(rendered1); - expect(mouseDidEnter.mock.instances.length).toBe(1); - expect(mouseDidEnter.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.mouseOver(rendered2); - expect(mouseDidEnter.mock.instances.length).toBe(2); - expect(mouseDidEnter.mock.instances[1]).toBe(mountedInstance2); - - ReactTestUtils.Simulate.mouseOut(rendered1); - expect(mouseDidLeave.mock.instances.length).toBe(1); - expect(mouseDidLeave.mock.instances[0]).toBe(mountedInstance1); - - ReactTestUtils.Simulate.mouseOut(rendered2); - expect(mouseDidLeave.mock.instances.length).toBe(2); - expect(mouseDidLeave.mock.instances[1]).toBe(mountedInstance2); - }); - - it('should not hold reference to instance', () => { - var mouseDidClick = function() { - void this.state.something; - }; - - var TestBindComponent = React.createClass({ - autobind: false, - getInitialState: function() { - return {something: 'hi'}; - }, - onClick: mouseDidClick, - - // auto binding only occurs on top level functions in class defs. - badIdeas: { - badBind: function() { - void this.state.something; - }, - }, - - render: function() { - return ( -
- ); - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - var instance2 = ; - var mountedInstance2 = ReactTestUtils.renderIntoDocument(instance2); - var rendered2 = reactComponentExpect(mountedInstance2) - .expectRenderedChild() - .instance(); - - expect(function() { - var badIdea = instance1.badIdeas.badBind; - badIdea(); - }).toThrow(); - - expect(mountedInstance1.onClick).toBe(mountedInstance2.onClick); - - expect(function() { - ReactTestUtils.Simulate.click(rendered1); - }).toThrow(); - - expect(function() { - ReactTestUtils.Simulate.click(rendered2); - }).toThrow(); - }); - - it('works with mixins that have not opted out of autobinding', () => { - var mouseDidClick = jest.fn(); - - var TestMixin = { - onClick: mouseDidClick, - }; - - var TestBindComponent = React.createClass({ - mixins: [TestMixin], - - render: function() { - return
; - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - ReactTestUtils.Simulate.click(rendered1); - expect(mouseDidClick.mock.instances.length).toBe(1); - expect(mouseDidClick.mock.instances[0]).toBe(mountedInstance1); - }); - - it('works with mixins that have opted out of autobinding', () => { - var mouseDidClick = jest.fn(); - - var TestMixin = { - autobind: false, - onClick: mouseDidClick, - }; - - var TestBindComponent = React.createClass({ - mixins: [TestMixin], - - render: function() { - return
; - }, - }); - - var instance1 = ; - var mountedInstance1 = ReactTestUtils.renderIntoDocument(instance1); - var rendered1 = reactComponentExpect(mountedInstance1) - .expectRenderedChild() - .instance(); - - ReactTestUtils.Simulate.click(rendered1); - expect(mouseDidClick.mock.instances.length).toBe(1); - expect(mouseDidClick.mock.instances[0]).toBe(mountedInstance1); - }); - - it('does not warn if you try to bind to this', () => { - spyOn(console, 'error'); - - var TestBindComponent = React.createClass({ - autobind: false, - handleClick: function() { }, - render: function() { - return
; - }, - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.error.calls.count()).toBe(0); - }); - - it('does not warn if you pass an manually bound method to setState', () => { - spyOn(console, 'error'); - - var TestBindComponent = React.createClass({ - autobind: false, - getInitialState: function() { - return {foo: 1}; - }, - componentDidMount: function() { - this.setState({foo: 2}, this.handleUpdate.bind(this)); - }, - handleUpdate: function() { - - }, - render: function() { - return
; - }, - }); - - ReactTestUtils.renderIntoDocument(); - - expect(console.error.calls.count()).toBe(0); - }); - -}); diff --git a/src/isomorphic/classic/class/__tests__/ReactClass-test.js b/src/isomorphic/classic/class/__tests__/ReactClass-test.js index e56d3913ba8d3..e992af7429b45 100644 --- a/src/isomorphic/classic/class/__tests__/ReactClass-test.js +++ b/src/isomorphic/classic/class/__tests__/ReactClass-test.js @@ -23,6 +23,26 @@ describe('ReactClass-spec', () => { ReactTestUtils = require('ReactTestUtils'); }); + it('should warn on first call to React.createClass', () => { + spyOn(console, 'error'); + const spec = { + render() { + return
; + }, + }; + React.createClass(spec); + React.createClass(spec); + expect(console.error.calls.count()).toEqual(1); + expect(console.error.calls.count()).toEqual(1); + expect(console.error.calls.argsFor(0)[0]).toBe( + 'Warning: React.createClass is deprecated and will be removed in ' + + 'version 16. Use plain JavaScript classes instead. If you\'re not yet ' + + 'ready to migrate, react-create-class is available on npm as a ' + + 'drop-in replacement.' + ); + console.error.calls.reset(); + }); + it('should throw when `render` is not specified', () => { expect(function() { React.createClass({}); diff --git a/src/isomorphic/classic/class/__tests__/ReactClassMixin-test.js b/src/isomorphic/classic/class/__tests__/ReactClassMixin-test.js deleted file mode 100644 index 3a3bb6288fa0b..0000000000000 --- a/src/isomorphic/classic/class/__tests__/ReactClassMixin-test.js +++ /dev/null @@ -1,545 +0,0 @@ -/** - * Copyright 2013-present, 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 ReactTestUtils; - -var TestComponent; -var TestComponentWithPropTypes; -var TestComponentWithReverseSpec; -var mixinPropValidator; -var componentPropValidator; - -describe('ReactClass-mixin', () => { - - beforeEach(() => { - React = require('React'); - ReactTestUtils = require('ReactTestUtils'); - mixinPropValidator = jest.fn(); - componentPropValidator = jest.fn(); - - var MixinA = { - propTypes: { - propA: function() {}, - }, - componentDidMount: function() { - this.props.listener('MixinA didMount'); - }, - }; - - var MixinB = { - mixins: [MixinA], - propTypes: { - propB: function() {}, - }, - componentDidMount: function() { - this.props.listener('MixinB didMount'); - }, - }; - - var MixinBWithReverseSpec = { - componentDidMount: function() { - this.props.listener('MixinBWithReverseSpec didMount'); - }, - mixins: [MixinA], - }; - - var MixinC = { - statics: { - staticC: function() {}, - }, - componentDidMount: function() { - this.props.listener('MixinC didMount'); - }, - }; - - var MixinD = { - propTypes: { - value: mixinPropValidator, - }, - }; - - TestComponent = React.createClass({ - mixins: [MixinB, MixinC, MixinD], - statics: { - staticComponent: function() {}, - }, - propTypes: { - propComponent: function() {}, - }, - componentDidMount: function() { - this.props.listener('Component didMount'); - }, - render: function() { - return
; - }, - }); - - TestComponentWithReverseSpec = React.createClass({ - render: function() { - return
; - }, - componentDidMount: function() { - this.props.listener('Component didMount'); - }, - mixins: [MixinBWithReverseSpec, MixinC, MixinD], - }); - - TestComponentWithPropTypes = React.createClass({ - mixins: [MixinD], - propTypes: { - value: componentPropValidator, - }, - render: function() { - return
; - }, - }); - }); - - it('should support merging propTypes and statics', () => { - var listener = jest.fn(); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - - var instancePropTypes = instance.constructor.propTypes; - - expect('propA' in instancePropTypes).toBe(true); - expect('propB' in instancePropTypes).toBe(true); - expect('propComponent' in instancePropTypes).toBe(true); - - expect('staticC' in TestComponent).toBe(true); - expect('staticComponent' in TestComponent).toBe(true); - }); - - it('should support chaining delegate functions', () => { - var listener = jest.fn(); - var instance = ; - ReactTestUtils.renderIntoDocument(instance); - - expect(listener.mock.calls).toEqual([ - ['MixinA didMount'], - ['MixinB didMount'], - ['MixinC didMount'], - ['Component didMount'], - ]); - }); - - it('should chain functions regardless of spec property order', () => { - var listener = jest.fn(); - var instance = ; - ReactTestUtils.renderIntoDocument(instance); - - expect(listener.mock.calls).toEqual([ - ['MixinA didMount'], - ['MixinBWithReverseSpec didMount'], - ['MixinC didMount'], - ['Component didMount'], - ]); - }); - - it('should validate prop types via mixins', () => { - expect(TestComponent.propTypes).toBeDefined(); - expect(TestComponent.propTypes.value) - .toBe(mixinPropValidator); - }); - - it('should override mixin prop types with class prop types', () => { - // Sanity check... - expect(componentPropValidator).not.toBe(mixinPropValidator); - // Actually check... - expect(TestComponentWithPropTypes.propTypes) - .toBeDefined(); - expect(TestComponentWithPropTypes.propTypes.value) - .not.toBe(mixinPropValidator); - expect(TestComponentWithPropTypes.propTypes.value) - .toBe(componentPropValidator); - }); - - - it('should support mixins with getInitialState()', () => { - var Mixin = { - getInitialState: function() { - return {mixin: true}; - }, - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return {component: true}; - }, - render: function() { - return ; - }, - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state.component).toBe(true); - expect(instance.state.mixin).toBe(true); - }); - - it('should throw with conflicting getInitialState() methods', () => { - var Mixin = { - getInitialState: function() { - return {x: true}; - }, - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return {x: true}; - }, - render: function() { - return ; - }, - }); - var instance = ; - expect(function() { - instance = ReactTestUtils.renderIntoDocument(instance); - }).toThrowError( - 'mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the ' + - 'same key: `x`. This conflict may be due to a mixin; in particular, ' + - 'this may be caused by two getInitialState() or getDefaultProps() ' + - 'methods returning objects with clashing keys.' - ); - }); - - it('should not mutate objects returned by getInitialState()', () => { - var Mixin = { - getInitialState: function() { - return Object.freeze({mixin: true}); - }, - }; - var Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return Object.freeze({component: true}); - }, - render: function() { - return ; - }, - }); - expect(() => { - ReactTestUtils.renderIntoDocument(); - }).not.toThrow(); - }); - - it('should support statics in mixins', () => { - var Mixin = { - statics: { - foo: 'bar', - }, - }; - var Component = React.createClass({ - mixins: [Mixin], - - statics: { - abc: 'def', - }, - - render: function() { - return ; - }, - }); - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.constructor.foo).toBe('bar'); - expect(Component.foo).toBe('bar'); - expect(instance.constructor.abc).toBe('def'); - expect(Component.abc).toBe('def'); - }); - - it("should throw if mixins override each others' statics", () => { - expect(function() { - var Mixin = { - statics: { - abc: 'foo', - }, - }; - React.createClass({ - mixins: [Mixin], - - statics: { - abc: 'bar', - }, - - render: function() { - return ; - }, - }); - }).toThrowError( - 'ReactClass: You are attempting to define `abc` on your component more ' + - 'than once. This conflict may be due to a mixin.' - ); - }); - - it('should throw if mixins override functions in statics', () => { - expect(function() { - var Mixin = { - statics: { - abc: function() { - console.log('foo'); - }, - }, - }; - React.createClass({ - mixins: [Mixin], - - statics: { - abc: function() { - console.log('bar'); - }, - }, - - render: function() { - return ; - }, - }); - }).toThrowError( - 'ReactClass: You are attempting to define `abc` on your component ' + - 'more than once. This conflict may be due to a mixin.' - ); - }); - - it('should warn if the mixin is undefined', () => { - spyOn(console, 'error'); - - React.createClass({ - mixins: [undefined], - - render: function() { - return ; - }, - }); - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactClass: You\'re attempting to include a mixin that is ' + - 'either null or not an object. Check the mixins included by the ' + - 'component, as well as any mixins they include themselves. ' + - 'Expected object but got undefined.' - ); - }); - - it('should warn if the mixin is null', () => { - spyOn(console, 'error'); - - React.createClass({ - mixins: [null], - - render: function() { - return ; - }, - }); - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactClass: You\'re attempting to include a mixin that is ' + - 'either null or not an object. Check the mixins included by the ' + - 'component, as well as any mixins they include themselves. ' + - 'Expected object but got null.' - ); - }); - - it('should warn if an undefined mixin is included in another mixin', () => { - spyOn(console, 'error'); - - var mixinA = { - mixins: [undefined], - }; - - React.createClass({ - mixins: [mixinA], - - render: function() { - return ; - }, - }); - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactClass: You\'re attempting to include a mixin that is ' + - 'either null or not an object. Check the mixins included by the ' + - 'component, as well as any mixins they include themselves. ' + - 'Expected object but got undefined.' - ); - }); - - it('should warn if a null mixin is included in another mixin', () => { - spyOn(console, 'error'); - - var mixinA = { - mixins: [null], - }; - - React.createClass({ - mixins: [mixinA], - - render: function() { - return ; - }, - }); - - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactClass: You\'re attempting to include a mixin that is ' + - 'either null or not an object. Check the mixins included by the ' + - 'component, as well as any mixins they include themselves. ' + - 'Expected object but got null.' - ); - }); - - it('should throw if the mixin is a React component', () => { - expect(function() { - React.createClass({ - mixins: [
], - - render: function() { - return ; - }, - }); - }).toThrowError( - 'ReactClass: You\'re attempting to use a component as a mixin. ' + - 'Instead, just use a regular object.' - ); - }); - - it('should throw if the mixin is a React component class', () => { - expect(function() { - var Component = React.createClass({ - render: function() { - return ; - }, - }); - - React.createClass({ - mixins: [Component], - - render: function() { - return ; - }, - }); - }).toThrowError( - 'ReactClass: You\'re attempting to use a component class or function ' + - 'as a mixin. Instead, just use a regular object.' - ); - }); - - it('should have bound the mixin methods to the component', () => { - var mixin = { - mixinFunc: function() { - return this; - }, - }; - - var Component = React.createClass({ - mixins: [mixin], - componentDidMount: function() { - expect(this.mixinFunc()).toBe(this); - }, - render: function() { - return ; - }, - }); - var instance = ; - ReactTestUtils.renderIntoDocument(instance); - }); - - it('should include the mixin keys in even if their values are falsy', () => { - var mixin = { - keyWithNullValue: null, - randomCounter: 0, - }; - - var Component = React.createClass({ - mixins: [mixin], - componentDidMount: function() { - expect(this.randomCounter).toBe(0); - expect(this.keyWithNullValue).toBeNull(); - }, - render: function() { - return ; - }, - }); - var instance = ; - ReactTestUtils.renderIntoDocument(instance); - }); - - it('should work with a null getInitialState return value and a mixin', () => { - var Component; - var instance; - - var Mixin = { - getInitialState: function() { - return {foo: 'bar'}; - }, - }; - Component = React.createClass({ - mixins: [Mixin], - getInitialState: function() { - return null; - }, - render: function() { - return ; - }, - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar'}); - - // Also the other way round should work - var Mixin2 = { - getInitialState: function() { - return null; - }, - }; - Component = React.createClass({ - mixins: [Mixin2], - getInitialState: function() { - return {foo: 'bar'}; - }, - render: function() { - return ; - }, - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar'}); - - // Multiple mixins should be fine too - Component = React.createClass({ - mixins: [Mixin, Mixin2], - getInitialState: function() { - return {x: true}; - }, - render: function() { - return ; - }, - }); - expect( - () => ReactTestUtils.renderIntoDocument() - ).not.toThrow(); - - instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'bar', x: true}); - }); - -}); diff --git a/src/isomorphic/classic/element/__tests__/ReactElement-test.js b/src/isomorphic/classic/element/__tests__/ReactElement-test.js index 36814982c3f1d..cf82a953094ac 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElement-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElement-test.js @@ -32,11 +32,11 @@ describe('ReactElement', () => { ReactTestUtils = require('ReactTestUtils'); // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. - ComponentClass = React.createClass({ - render: function() { + ComponentClass = class extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + }; }); afterEach(() => { @@ -57,37 +57,7 @@ describe('ReactElement', () => { expect(element.props).toEqual({}); }); - it('should warn when `key` is being accessed on createClass element', () => { - spyOn(console, 'error'); - var container = document.createElement('div'); - var Child = React.createClass({ - render: function() { - return
{this.props.key}
; - }, - }); - var Parent = React.createClass({ - render: function() { - return ( -
- - - -
- ); - }, - }); - expect(console.error.calls.count()).toBe(0); - ReactDOM.render(, container); - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.argsFor(0)[0]).toContain( - 'Child: `key` is not a prop. Trying to access it will result ' + - 'in `undefined` being returned. If you need to access the same ' + - 'value within the child component, you should pass it as a different ' + - 'prop. (https://fb.me/react-special-props)' - ); - }); - - it('should warn when `key` is being accessed on ES class element', () => { + it('should warn when `key` is being accessed on composite element', () => { spyOn(console, 'error'); var container = document.createElement('div'); class Child extends React.Component { @@ -95,8 +65,8 @@ describe('ReactElement', () => { return
{this.props.key}
; } } - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { return (
@@ -104,8 +74,8 @@ describe('ReactElement', () => {
); - }, - }); + } + } expect(console.error.calls.count()).toBe(0); ReactDOM.render(, container); expect(console.error.calls.count()).toBe(1); @@ -134,20 +104,20 @@ describe('ReactElement', () => { it('should warn when `ref` is being accessed', () => { spyOn(console, 'error'); var container = document.createElement('div'); - var Child = React.createClass({ - render: function() { + class Child extends React.Component { + render() { return
{this.props.ref}
; - }, - }); - var Parent = React.createClass({ - render: function() { + } + } + class Parent extends React.Component { + render() { return (
); - }, - }); + } + } expect(console.error.calls.count()).toBe(0); ReactDOM.render(, container); expect(console.error.calls.count()).toBe(1); @@ -255,12 +225,12 @@ describe('ReactElement', () => { var Component = React.createFactory(ComponentClass); var element; - var Wrapper = React.createClass({ - render: function() { + class Wrapper extends React.Component { + render() { element = Component(); return element; - }, - }); + } + } var instance = ReactTestUtils.renderIntoDocument( React.createElement(Wrapper) @@ -312,19 +282,12 @@ describe('ReactElement', () => { it('allows static methods to be called using the type property', () => { spyOn(console, 'error'); - var StaticMethodComponentClass = React.createClass({ - statics: { - someStaticMethod: function() { - return 'someReturnValue'; - }, - }, - getInitialState: function() { - return {valueToReturn: 'hi'}; - }, - render: function() { + class StaticMethodComponentClass extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + } + StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue'; var element = React.createElement(StaticMethodComponentClass); expect(element.type.someStaticMethod()).toBe('someReturnValue'); @@ -334,11 +297,11 @@ describe('ReactElement', () => { // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. it('identifies valid elements', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + } expect(React.isValidElement(React.createElement('div'))) .toEqual(true); @@ -357,21 +320,6 @@ describe('ReactElement', () => { expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true); }); - it('allows the use of PropTypes validators in statics', () => { - // TODO: This test was added to cover a special case where we proxied - // methods. However, we don't do that any more so this test can probably - // be removed. Leaving it in classic as a safety precaution. - var Component = React.createClass({ - render: () => null, - statics: { - specialType: React.PropTypes.shape({monkey: React.PropTypes.any}), - }, - }); - - expect(typeof Component.specialType).toBe('function'); - expect(typeof Component.specialType.isRequired).toBe('function'); - }); - // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. it('is indistinguishable from a plain object', () => { @@ -383,14 +331,12 @@ describe('ReactElement', () => { // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. it('should use default prop value when removing a prop', () => { - var Component = React.createClass({ - getDefaultProps: function() { - return {fruit: 'persimmon'}; - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span'); - }, - }); + } + } + Component.defaultProps = {fruit: 'persimmon'}; var container = document.createElement('div'); var instance = ReactDOM.render( @@ -406,14 +352,12 @@ describe('ReactElement', () => { // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. it('should normalize props with default values', () => { - var Component = React.createClass({ - getDefaultProps: function() { - return {prop: 'testKey'}; - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span', null, this.props.prop); - }, - }); + } + } + Component.defaultProps = {prop: 'testKey'}; var instance = ReactTestUtils.renderIntoDocument( React.createElement(Component) @@ -427,8 +371,8 @@ describe('ReactElement', () => { }); it('throws when changing a prop (in dev) after element creation', () => { - var Outer = React.createClass({ - render: function() { + class Outer extends React.Component { + render() { var el =
; expect(function() { @@ -437,17 +381,16 @@ describe('ReactElement', () => { expect(el.props.className).toBe('moo'); return el; - }, - }); + } + } var outer = ReactTestUtils.renderIntoDocument(); expect(ReactDOM.findDOMNode(outer).className).toBe('moo'); }); it('throws when adding a prop (in dev) after element creation', () => { var container = document.createElement('div'); - var Outer = React.createClass({ - getDefaultProps: () => ({sound: 'meow'}), - render: function() { + class Outer extends React.Component { + render() { var el =
{this.props.sound}
; expect(function() { @@ -457,8 +400,9 @@ describe('ReactElement', () => { expect(el.props.className).toBe(undefined); return el; - }, - }); + } + } + Outer.defaultProps = {sound: 'meow'}; var outer = ReactDOM.render(, container); expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow'); expect(ReactDOM.findDOMNode(outer).className).toBe(''); @@ -466,11 +410,11 @@ describe('ReactElement', () => { it('does not warn for NaN props', () => { spyOn(console, 'error'); - var Test = React.createClass({ - render: function() { + class Test extends React.Component { + render() { return
; - }, - }); + } + } var test = ReactTestUtils.renderIntoDocument(); expect(test.props.value).toBeNaN(); expect(console.error.calls.count()).toBe(0); @@ -498,11 +442,11 @@ describe('ReactElement', () => { React = require('React'); - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + } expect(React.isValidElement(React.createElement('div'))) .toEqual(true); @@ -538,15 +482,15 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { describe('when using jsx only', () => { var Parent, instance; beforeEach(() => { - Parent = React.createClass({ - render: function() { + Parent = class extends React.Component { + render() { return (
children value
); - }, - }); + } + }; instance = ReactTestUtils.renderIntoDocument(); }); @@ -568,11 +512,11 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { var factory, instance; beforeEach(() => { var childFactory = React.createFactory(Child); - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { return React.DOM.div({}, childFactory({ ref: 'child', foo: 'foo value' }, 'children value')); - }, - }); + } + } factory = React.createFactory(Parent); instance = ReactTestUtils.renderIntoDocument(factory()); }); @@ -594,11 +538,11 @@ describe('comparing jsx vs .createFactory() vs .createElement()', () => { describe('when using parent that uses .createElement()', () => { var factory, instance; beforeEach(() => { - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { return React.DOM.div({}, React.createElement(Child, { ref: 'child', foo: 'foo value' }, 'children value')); - }, - }); + } + } factory = React.createFactory(Parent); instance = ReactTestUtils.renderIntoDocument(factory()); }); diff --git a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js index 50406d03456d6..69e8984ca2f8b 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElementClone-test.js @@ -25,52 +25,52 @@ describe('ReactElementClone', () => { // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. - ComponentClass = React.createClass({ - render: function() { + ComponentClass = class extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + }; }); it('should clone a DOM component with new props', () => { - var Grandparent = React.createClass({ - render: function() { + class Grandparent extends React.Component { + render() { return } />; - }, - }); - var Parent = React.createClass({ - render: function() { + } + } + class Parent extends React.Component { + render() { return (
{React.cloneElement(this.props.child, { className: 'xyz' })}
); - }, - }); + } + } var component = ReactTestUtils.renderIntoDocument(); expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz'); }); it('should clone a composite component with new props', () => { - var Child = React.createClass({ - render: function() { + class Child extends React.Component { + render() { return
; - }, - }); - var Grandparent = React.createClass({ - render: function() { + } + } + class Grandparent extends React.Component { + render() { return } />; - }, - }); - var Parent = React.createClass({ - render: function() { + } + } + class Parent extends React.Component { + render() { return (
{React.cloneElement(this.props.child, { className: 'xyz' })}
); - }, - }); + } + } var component = ReactTestUtils.renderIntoDocument(); expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz'); }); @@ -81,43 +81,43 @@ describe('ReactElementClone', () => { }); it('should keep the original ref if it is not overridden', () => { - var Grandparent = React.createClass({ - render: function() { + class Grandparent extends React.Component { + render() { return } />; - }, - }); + } + } - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { return (
{React.cloneElement(this.props.child, { className: 'xyz' })}
); - }, - }); + } + } var component = ReactTestUtils.renderIntoDocument(); expect(component.refs.yolo.tagName).toBe('DIV'); }); it('should transfer the key property', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { return null; - }, - }); + } + } var clone = React.cloneElement(, {key: 'xyz'}); expect(clone.key).toBe('xyz'); }); it('should transfer children', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { expect(this.props.children).toBe('xyz'); return
; - }, - }); + } + } ReactTestUtils.renderIntoDocument( React.cloneElement(, {children: 'xyz'}) @@ -125,12 +125,12 @@ describe('ReactElementClone', () => { }); it('should shallow clone children', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { expect(this.props.children).toBe('xyz'); return
; - }, - }); + } + } ReactTestUtils.renderIntoDocument( React.cloneElement(xyz, {}) @@ -138,11 +138,11 @@ describe('ReactElementClone', () => { }); it('should accept children as rest arguments', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { return null; - }, - }); + } + } var clone = React.cloneElement( xyz, @@ -170,39 +170,39 @@ describe('ReactElementClone', () => { }); it('should support keys and refs', () => { - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { var clone = React.cloneElement(this.props.children, {key: 'xyz', ref: 'xyz'}); expect(clone.key).toBe('xyz'); expect(clone.ref).toBe('xyz'); return
{clone}
; - }, - }); + } + } - var Grandparent = React.createClass({ - render: function() { + class Grandparent extends React.Component { + render() { return ; - }, - }); + } + } var component = ReactTestUtils.renderIntoDocument(); expect(component.refs.parent.refs.xyz.tagName).toBe('SPAN'); }); it('should steal the ref if a new ref is specified', () => { - var Parent = React.createClass({ - render: function() { + class Parent extends React.Component { + render() { var clone = React.cloneElement(this.props.children, {ref: 'xyz'}); return
{clone}
; - }, - }); + } + } - var Grandparent = React.createClass({ - render: function() { + class Grandparent extends React.Component { + render() { return ; - }, - }); + } + } var component = ReactTestUtils.renderIntoDocument(); expect(component.refs.child).toBeUndefined(); @@ -210,12 +210,12 @@ describe('ReactElementClone', () => { }); it('should overwrite props', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { expect(this.props.myprop).toBe('xyz'); return
; - }, - }); + } + } ReactTestUtils.renderIntoDocument( React.cloneElement(, {myprop: 'xyz'}) @@ -223,14 +223,12 @@ describe('ReactElementClone', () => { }); it('should normalize props with default values', () => { - var Component = React.createClass({ - getDefaultProps: function() { - return {prop: 'testKey'}; - }, - render: function() { + class Component extends React.Component { + render() { return ; - }, - }); + } + } + Component.defaultProps = {prop: 'testKey'}; var instance = React.createElement(Component); var clonedInstance = React.cloneElement(instance, {prop: undefined}); @@ -282,27 +280,27 @@ describe('ReactElementClone', () => { it('should check declared prop types after clone', () => { spyOn(console, 'error'); - var Component = React.createClass({ - propTypes: { - color: React.PropTypes.string.isRequired, - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('div', null, 'My color is ' + this.color); - }, - }); - var Parent = React.createClass({ - render: function() { + } + } + Component.propTypes = { + color: React.PropTypes.string.isRequired, + }; + class Parent extends React.Component { + render() { return React.cloneElement(this.props.child, {color: 123}); - }, - }); - var GrandParent = React.createClass({ - render: function() { + } + } + class GrandParent extends React.Component { + render() { return React.createElement( Parent, { child: React.createElement(Component, {color: 'red'}) } ); - }, - }); + } + } ReactTestUtils.renderIntoDocument(React.createElement(GrandParent)); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( diff --git a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js index b36fa2f39c9ab..17367f0439c90 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js @@ -31,11 +31,11 @@ describe('ReactElementValidator', () => { React = require('React'); ReactDOM = require('ReactDOM'); ReactTestUtils = require('ReactTestUtils'); - ComponentClass = React.createClass({ - render: function() { + ComponentClass = class extends React.Component { + render() { return React.createElement('div'); - }, - }); + } + }; }); it('warns for keys for arrays of elements in rest args', () => { @@ -54,21 +54,19 @@ describe('ReactElementValidator', () => { spyOn(console, 'error'); var Component = React.createFactory(ComponentClass); - var InnerClass = React.createClass({ - displayName: 'InnerClass', - render: function() { + class InnerClass extends React.Component { + render() { return Component(null, this.props.childSet); - }, - }); + } + } var InnerComponent = React.createFactory(InnerClass); - var ComponentWrapper = React.createClass({ - displayName: 'ComponentWrapper', - render: function() { + class ComponentWrapper extends React.Component { + render() { return InnerComponent({childSet: [Component(), Component()] }); - }, - }); + } + } ReactTestUtils.renderIntoDocument( React.createElement(ComponentWrapper) @@ -85,12 +83,10 @@ describe('ReactElementValidator', () => { it('warns for keys for arrays with no owner or parent info', () => { spyOn(console, 'error'); - var Anonymous = React.createClass({ - displayName: undefined, - render: function() { - return
; - }, - }); + function Anonymous() { + return
; + } + Object.defineProperty(Anonymous, 'name', { value: undefined }); var divs = [
, @@ -127,23 +123,17 @@ describe('ReactElementValidator', () => { it('warns for keys with component stack info', () => { spyOn(console, 'error'); - var Component = React.createClass({ - render: function() { - return
{[
,
]}
; - }, - }); + function Component() { + return
{[
,
]}
; + } - var Parent = React.createClass({ - render: function() { - return React.cloneElement(this.props.child); - }, - }); + function Parent(props) { + return React.cloneElement(props.child); + } - var GrandParent = React.createClass({ - render: function() { - return } />; - }, - }); + function GrandParent() { + return } />; + } ReactTestUtils.renderIntoDocument(); @@ -162,16 +152,14 @@ describe('ReactElementValidator', () => { it('does not warn for keys when passing children down', () => { spyOn(console, 'error'); - var Wrapper = React.createClass({ - render: function() { - return ( -
- {this.props.children} -
-
- ); - }, - }); + function Wrapper(props) { + return ( +
+ {props.children} +
+
+ ); + } ReactTestUtils.renderIntoDocument( @@ -266,19 +254,15 @@ describe('ReactElementValidator', () => { // component, we give a small hint as to which parent instantiated that // component as per warnings about key usage in ReactElementValidator. spyOn(console, 'error'); - var MyComp = React.createClass({ - propTypes: { - color: React.PropTypes.string, - }, - render: function() { - return React.createElement('div', null, 'My color is ' + this.color); - }, - }); - var ParentComp = React.createClass({ - render: function() { - return React.createElement(MyComp, {color: 123}); - }, - }); + function MyComp(props) { + return React.createElement('div', null, 'My color is ' + props.color); + } + MyComp.propTypes = { + color: React.PropTypes.string, + }; + function ParentComp() { + return React.createElement(MyComp, {color: 123}); + } ReactTestUtils.renderIntoDocument(React.createElement(ParentComp)); expect(console.error.calls.argsFor(0)[0]).toBe( 'Warning: Failed prop type: ' + @@ -336,11 +320,9 @@ describe('ReactElementValidator', () => { it('includes the owner name when passing null, undefined, boolean, or number', () => { spyOn(console, 'error'); - var ParentComp = React.createClass({ - render: function() { - return React.createElement(null); - }, - }); + function ParentComp() { + return React.createElement(null); + } expect(function() { ReactTestUtils.renderIntoDocument(React.createElement(ParentComp)); }).toThrowError( @@ -359,15 +341,13 @@ describe('ReactElementValidator', () => { it('should check default prop values', () => { spyOn(console, 'error'); - var Component = React.createClass({ - propTypes: {prop: React.PropTypes.string.isRequired}, - getDefaultProps: function() { - return {prop: null}; - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span', null, this.props.prop); - }, - }); + } + } + Component.propTypes = {prop: React.PropTypes.string.isRequired}; + Component.defaultProps = {prop: null}; ReactTestUtils.renderIntoDocument(React.createElement(Component)); @@ -382,15 +362,13 @@ describe('ReactElementValidator', () => { it('should not check the default for explicit null', () => { spyOn(console, 'error'); - var Component = React.createClass({ - propTypes: {prop: React.PropTypes.string.isRequired}, - getDefaultProps: function() { - return {prop: 'text'}; - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span', null, this.props.prop); - }, - }); + } + } + Component.propTypes = {prop: React.PropTypes.string.isRequired}; + Component.defaultProps = {prop: 'text'}; ReactTestUtils.renderIntoDocument( React.createElement(Component, {prop:null}) @@ -407,14 +385,14 @@ describe('ReactElementValidator', () => { it('should check declared prop types', () => { spyOn(console, 'error'); - var Component = React.createClass({ - propTypes: { - prop: React.PropTypes.string.isRequired, - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span', null, this.props.prop); - }, - }); + } + } + Component.propTypes = { + prop: React.PropTypes.string.isRequired, + }; ReactTestUtils.renderIntoDocument( React.createElement(Component) @@ -449,14 +427,15 @@ describe('ReactElementValidator', () => { it('should warn if a PropType creator is used as a PropType', () => { spyOn(console, 'error'); - var Component = React.createClass({ - propTypes: { - myProp: React.PropTypes.shape, - }, - render: function() { + class Component extends React.Component { + render() { return React.createElement('span', null, this.props.myProp.value); - }, - }); + + } + } + Component.propTypes = { + myProp: React.PropTypes.shape, + }; ReactTestUtils.renderIntoDocument( React.createElement(Component, {myProp: {value: 'hi'}}) @@ -474,11 +453,9 @@ describe('ReactElementValidator', () => { it('should warn when accessing .type on an element factory', () => { spyOn(console, 'error'); - var TestComponent = React.createClass({ - render: function() { - return
; - }, - }); + function TestComponent() { + return
; + } var TestFactory = React.createFactory(TestComponent); expect(TestFactory.type).toBe(TestComponent); expect(console.error.calls.count()).toBe(1); @@ -493,14 +470,14 @@ describe('ReactElementValidator', () => { it('does not warn when using DOM node as children', () => { spyOn(console, 'error'); - var DOMContainer = React.createClass({ - render: function() { + class DOMContainer extends React.Component { + render() { return
; - }, - componentDidMount: function() { + } + componentDidMount() { ReactDOM.findDOMNode(this).appendChild(this.props.children); - }, - }); + } + } var node = document.createElement('div'); // This shouldn't cause a stack overflow or any other problems (#3883) diff --git a/src/renderers/dom/client/__tests__/ReactMount-test.js b/src/renderers/dom/client/__tests__/ReactMount-test.js index 7e574216bdc7e..d9d9e5eff14a5 100644 --- a/src/renderers/dom/client/__tests__/ReactMount-test.js +++ b/src/renderers/dom/client/__tests__/ReactMount-test.js @@ -91,13 +91,13 @@ describe('ReactMount', () => { var mockMount = jest.fn(); var mockUnmount = jest.fn(); - var Component = React.createClass({ - componentDidMount: mockMount, - componentWillUnmount: mockUnmount, - render: function() { + class Component extends React.Component { + componentDidMount = mockMount; + componentWillUnmount = mockUnmount; + render() { return {this.props.text}; - }, - }); + } + } expect(mockMount.mock.calls.length).toBe(0); expect(mockUnmount.mock.calls.length).toBe(0); diff --git a/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js b/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js index d39527f5c0a04..5d2ec7ab88bc9 100644 --- a/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js +++ b/src/renderers/dom/client/eventPlugins/__tests__/BeforeInputEventPlugin-test.js @@ -184,9 +184,11 @@ describe('BeforeInputEventPlugin', function() { function TestEditableReactComponent(Emulator, Scenario, ExpectedResult) { ModuleCache = new initialize(Emulator); - var EditableDiv = React.createClass({ - render: () => (
), - }); + class EditableDiv extends React.Component { + render() { + return
; + } + } var rendered = ReactTestUtils.renderIntoDocument(); var node = ModuleCache.ReactDOM.findDOMNode(rendered); diff --git a/src/renderers/dom/server/__tests__/ReactServerRendering-test.js b/src/renderers/dom/server/__tests__/ReactServerRendering-test.js index 26db5640693b2..af931eb408d30 100644 --- a/src/renderers/dom/server/__tests__/ReactServerRendering-test.js +++ b/src/renderers/dom/server/__tests__/ReactServerRendering-test.js @@ -486,32 +486,6 @@ describe('ReactServerRendering', () => { expect(markup).toBe('
hello
'); }); - it('warns with a no-op when an async replaceState is triggered', () => { - var Bar = React.createClass({ - componentWillMount: function() { - this.replaceState({text: 'hello'}); - setTimeout(() => { - this.replaceState({text: 'error'}); - }); - }, - render: function() { - return
{}}>{this.state.text}
; - }, - }); - - spyOn(console, 'error'); - ReactServerRendering.renderToString(); - jest.runOnlyPendingTimers(); - expect(console.error.calls.count()).toBe(1); - expect(console.error.calls.mostRecent().args[0]).toBe( - 'Warning: replaceState(...): Can only update a mounting component. ' + - 'This usually means you called replaceState() outside componentWillMount() on the server. ' + - 'This is a no-op. Please check the code for the Bar component.' - ); - var markup = ReactServerRendering.renderToStaticMarkup(); - expect(markup).toBe('
hello
'); - }); - it('warns with a no-op when an async forceUpdate is triggered', () => { class Baz extends React.Component { componentWillMount() { diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js index 34e14145a79c7..71bb4cb1824be 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js @@ -187,18 +187,14 @@ describe('ReactDOMComponent', () => { spyOn(console, 'error'); var div = document.createElement('div'); - var One = React.createClass({ - render: function() { - return this.props.inline ? - : -
; - }, - }); - var Two = React.createClass({ - render: function() { - return
; - }, - }); + function One(props) { + return props.inline ? + : +
; + } + function Two() { + return
; + } ReactDOM.render(, div); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toBe( @@ -1256,12 +1252,12 @@ describe('ReactDOMComponent', () => { it('gives useful context in warnings', () => { spyOn(console, 'error'); - var Row = React.createClass({ - render: () => , - }); - var FancyRow = React.createClass({ - render: () => , - }); + function Row() { + return ; + } + function FancyRow() { + return ; + } class Table extends React.Component { render() { @@ -1275,24 +1271,24 @@ describe('ReactDOMComponent', () => { } } - var Viz1 = React.createClass({ - render: () =>
, - }); - var App1 = React.createClass({ - render: () => , - }); + function Viz1() { + return
; + } + function App1() { + return ; + } ReactTestUtils.renderIntoDocument(); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( 'See Viz1 > table > FancyRow > Row > tr.' ); - var Viz2 = React.createClass({ - render: () => , - }); - var App2 = React.createClass({ - render: () => , - }); + function Viz2() { + return ; + } + function App2() { + return ; + } ReactTestUtils.renderIntoDocument(); expect(console.error.calls.count()).toBe(2); expect(console.error.calls.argsFor(1)[0]).toContain( diff --git a/src/renderers/shared/__tests__/ReactPerf-test.js b/src/renderers/shared/__tests__/ReactPerf-test.js index dfd1e517fc2dc..25ffd77d7bc76 100644 --- a/src/renderers/shared/__tests__/ReactPerf-test.js +++ b/src/renderers/shared/__tests__/ReactPerf-test.js @@ -59,16 +59,16 @@ describe('ReactPerf', () => { } }; - LifeCycle = React.createClass({ - shouldComponentUpdate: emptyFunction.thatReturnsTrue, - componentWillMount: emptyFunction, - componentDidMount: emptyFunction, - componentWillReceiveProps: emptyFunction, - componentWillUpdate: emptyFunction, - componentDidUpdate: emptyFunction, - componentWillUnmount: emptyFunction, - render: emptyFunction.thatReturnsNull, - }); + LifeCycle = class extends React.Component { + shouldComponentUpdate = emptyFunction.thatReturnsTrue; + componentWillMount = emptyFunction; + componentDidMount = emptyFunction; + componentWillReceiveProps = emptyFunction; + componentWillUpdate = emptyFunction; + componentDidUpdate = emptyFunction; + componentWillUnmount = emptyFunction; + render = emptyFunction.thatReturnsNull; + }; }); afterEach(() => { diff --git a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js index f13573c91fdf9..24f0bddfa72ce 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js @@ -751,15 +751,13 @@ describe('ReactIncremental', () => { it('can replaceState', () => { let instance; - const Bar = React.createClass({ - getInitialState() { - instance = this; - return { a: 'a' }; - }, + class Bar extends React.Component { + state = {a: 'a'}; render() { + instance = this; return
{this.props.children}
; - }, - }); + } + } function Foo() { return ( @@ -773,7 +771,7 @@ describe('ReactIncremental', () => { ReactNoop.flush(); instance.setState({ b: 'b' }); instance.setState({ c: 'c' }); - instance.replaceState({ d: 'd' }); + instance.updater.enqueueReplaceState(instance, {d: 'd'}); ReactNoop.flush(); expect(instance.state).toEqual({ d: 'd' }); }); diff --git a/src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js b/src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js index 61fd34902d2e1..d639848ae2d6b 100644 --- a/src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js +++ b/src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js @@ -1743,8 +1743,15 @@ describe('ReactComponentTreeHook', () => { return addendum.replace(/\(at .+?:\d+\)/g, '(at **)'); } - var Anon = React.createClass({displayName: null, render: () => null}); - var Orange = React.createClass({render: () => null}); + function Anon() { + return null; + } + Object.defineProperty(Anon, 'name', { + value: null, + }); + function Orange() { + return null; + } expect(getAddendum()).toBe( '' diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactComponentLifeCycle-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactComponentLifeCycle-test.js index a8a139b015b61..c1addcafd7030 100644 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactComponentLifeCycle-test.js +++ b/src/renderers/shared/stack/reconciler/__tests__/ReactComponentLifeCycle-test.js @@ -245,23 +245,28 @@ describe('ReactComponentLifeCycle', () => { it('should correctly determine if a component is mounted', () => { spyOn(console, 'error'); - var Component = React.createClass({ - componentWillMount: function() { - expect(this.isMounted()).toBeFalsy(); - }, - componentDidMount: function() { - expect(this.isMounted()).toBeTruthy(); - }, - render: function() { - expect(this.isMounted()).toBeFalsy(); + class Component extends React.Component { + _isMounted() { + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + return this.updater.isMounted(this); + } + componentWillMount() { + expect(this._isMounted()).toBeFalsy(); + } + componentDidMount() { + expect(this._isMounted()).toBeTruthy(); + } + render() { + expect(this._isMounted()).toBeFalsy(); return
; - }, - }); + } + } var element = ; var instance = ReactTestUtils.renderIntoDocument(element); - expect(instance.isMounted()).toBeTruthy(); + expect(instance._isMounted()).toBeTruthy(); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( @@ -271,23 +276,28 @@ describe('ReactComponentLifeCycle', () => { it('should correctly determine if a null component is mounted', () => { spyOn(console, 'error'); - var Component = React.createClass({ - componentWillMount: function() { - expect(this.isMounted()).toBeFalsy(); - }, - componentDidMount: function() { - expect(this.isMounted()).toBeTruthy(); - }, - render: function() { - expect(this.isMounted()).toBeFalsy(); + class Component extends React.Component { + _isMounted() { + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + return this.updater.isMounted(this); + } + componentWillMount() { + expect(this._isMounted()).toBeFalsy(); + } + componentDidMount() { + expect(this._isMounted()).toBeTruthy(); + } + render() { + expect(this._isMounted()).toBeFalsy(); return null; - }, - }); + } + } var element = ; var instance = ReactTestUtils.renderIntoDocument(element); - expect(instance.isMounted()).toBeTruthy(); + expect(instance._isMounted()).toBeTruthy(); expect(console.error.calls.count()).toBe(1); expect(console.error.calls.argsFor(0)[0]).toContain( @@ -296,38 +306,38 @@ describe('ReactComponentLifeCycle', () => { }); it('isMounted should return false when unmounted', () => { - var Component = React.createClass({ - render: function() { + class Component extends React.Component { + render() { return
; - }, - }); + } + } var container = document.createElement('div'); var instance = ReactDOM.render(, container); - expect(instance.isMounted()).toBe(true); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + expect(instance.updater.isMounted(instance)).toBe(true); ReactDOM.unmountComponentAtNode(container); - expect(instance.isMounted()).toBe(false); + expect(instance.updater.isMounted(instance)).toBe(false); }); it('warns if findDOMNode is used inside render', () => { spyOn(console, 'error'); - var Component = React.createClass({ - getInitialState: function() { - return {isMounted: false}; - }, - componentDidMount: function() { + class Component extends React.Component { + state = {isMounted: false}; + componentDidMount() { this.setState({isMounted: true}); - }, - render: function() { + } + render() { if (this.state.isMounted) { expect(ReactDOM.findDOMNode(this).tagName).toBe('DIV'); } return
; - }, - }); + } + } ReactTestUtils.renderIntoDocument(); expect(console.error.calls.count()).toBe(1); @@ -539,30 +549,31 @@ describe('ReactComponentLifeCycle', () => { return true; }; }; - var Outer = React.createClass({ - render: function() { + class Outer extends React.Component { + componentWillMount = logger('outer componentWillMount'); + componentDidMount = logger('outer componentDidMount'); + componentWillReceiveProps = logger('outer componentWillReceiveProps'); + shouldComponentUpdate = logger('outer shouldComponentUpdate'); + componentWillUpdate = logger('outer componentWillUpdate'); + componentDidUpdate = logger('outer componentDidUpdate'); + componentWillUnmount = logger('outer componentWillUnmount'); + render() { return
; - }, - componentWillMount: logger('outer componentWillMount'), - componentDidMount: logger('outer componentDidMount'), - componentWillReceiveProps: logger('outer componentWillReceiveProps'), - shouldComponentUpdate: logger('outer shouldComponentUpdate'), - componentWillUpdate: logger('outer componentWillUpdate'), - componentDidUpdate: logger('outer componentDidUpdate'), - componentWillUnmount: logger('outer componentWillUnmount'), - }); - var Inner = React.createClass({ - render: function() { + } + } + + class Inner extends React.Component { + componentWillMount = logger('inner componentWillMount'); + componentDidMount = logger('inner componentDidMount'); + componentWillReceiveProps = logger('inner componentWillReceiveProps'); + shouldComponentUpdate = logger('inner shouldComponentUpdate'); + componentWillUpdate = logger('inner componentWillUpdate'); + componentDidUpdate = logger('inner componentDidUpdate'); + componentWillUnmount = logger('inner componentWillUnmount'); + render() { return {this.props.x}; - }, - componentWillMount: logger('inner componentWillMount'), - componentDidMount: logger('inner componentDidMount'), - componentWillReceiveProps: logger('inner componentWillReceiveProps'), - shouldComponentUpdate: logger('inner shouldComponentUpdate'), - componentWillUpdate: logger('inner componentWillUpdate'), - componentDidUpdate: logger('inner componentDidUpdate'), - componentWillUnmount: logger('inner componentWillUnmount'), - }); + } + } var container = document.createElement('div'); diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponent-test.js index f38711594015f..66323dc6ed8bf 100644 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponent-test.js @@ -169,65 +169,6 @@ describe('ReactCompositeComponent', () => { expect(instance.getAnchor().className).toBe(''); }); - it('should auto bind methods and values correctly', () => { - spyOn(console, 'error'); - - var ComponentClass = React.createClass({ - getInitialState: function() { - return {valueToReturn: 'hi'}; - }, - methodToBeExplicitlyBound: function() { - return this; - }, - methodAutoBound: function() { - return this; - }, - render: function() { - return
; - }, - }); - var instance = ; - - // Next, prove that once mounted, the scope is bound correctly to the actual - // component. - var mountedInstance = ReactTestUtils.renderIntoDocument(instance); - - expect(function() { - mountedInstance.methodToBeExplicitlyBound.bind(instance)(); - }).not.toThrow(); - expect(function() { - mountedInstance.methodAutoBound(); - }).not.toThrow(); - - expect(console.error.calls.count()).toBe(1); - var explicitlyBound = mountedInstance.methodToBeExplicitlyBound.bind( - mountedInstance - ); - expect(console.error.calls.count()).toBe(2); - var autoBound = mountedInstance.methodAutoBound; - - var context = {}; - expect(explicitlyBound.call(context)).toBe(mountedInstance); - expect(autoBound.call(context)).toBe(mountedInstance); - - expect(explicitlyBound.call(mountedInstance)).toBe(mountedInstance); - expect(autoBound.call(mountedInstance)).toBe(mountedInstance); - - }); - - it('should not pass this to getDefaultProps', () => { - var Component = React.createClass({ - getDefaultProps: function() { - expect(this.render).not.toBeDefined(); - return {}; - }, - render: function() { - return
; - }, - }); - ReactTestUtils.renderIntoDocument(); - }); - it('should use default values for undefined props', () => { class Component extends React.Component { static defaultProps = {prop: 'testKey'}; @@ -1178,17 +1119,17 @@ describe('ReactCompositeComponent', () => { }); it('should replace state', () => { - var Moo = React.createClass({ - getInitialState: function() { - return {x: 1}; - }, - render: function() { + class Moo extends React.Component { + state = {x: 1}; + render() { return
; - }, - }); + } + } var moo = ReactTestUtils.renderIntoDocument(); - moo.replaceState({y: 2}); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + moo.updater.enqueueReplaceState(moo, {y: 2}); expect('x' in moo.state).toBe(false); expect(moo.state.y).toBe(2); }); @@ -1200,21 +1141,22 @@ describe('ReactCompositeComponent', () => { NotActuallyImmutable.prototype.amIImmutable = function() { return true; }; - var Moo = React.createClass({ - getInitialState: function() { - return new NotActuallyImmutable('first'); - }, - render: function() { + class Moo extends React.Component { + state = new NotActuallyImmutable('first'); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + _replaceState = update => this.updater.enqueueReplaceState(this, update); + render() { return
; - }, - }); + } + } var moo = ReactTestUtils.renderIntoDocument(); expect(moo.state.str).toBe('first'); expect(moo.state.amIImmutable()).toBe(true); var secondState = new NotActuallyImmutable('second'); - moo.replaceState(secondState); + moo._replaceState(secondState); expect(moo.state.str).toBe('second'); expect(moo.state.amIImmutable()).toBe(true); expect(moo.state).toBe(secondState); @@ -1228,14 +1170,14 @@ describe('ReactCompositeComponent', () => { var fifthState = new NotActuallyImmutable('fifth'); ReactUpdates.batchedUpdates(function() { moo.setState({str: 'fourth'}); - moo.replaceState(fifthState); + moo._replaceState(fifthState); }); expect(moo.state).toBe(fifthState); // When more than one state update is enqueued, we have the same behavior var sixthState = new NotActuallyImmutable('sixth'); ReactUpdates.batchedUpdates(function() { - moo.replaceState(sixthState); + moo._replaceState(sixthState); moo.setState({str: 'seventh'}); }); expect(moo.state.str).toBe('seventh'); diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js index c758aa5d2e665..3385d7a4d5276 100644 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js +++ b/src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js @@ -23,34 +23,34 @@ describe('ReactCompositeComponent-state', () => { ReactDOM = require('ReactDOM'); - TestComponent = React.createClass({ - peekAtState: function(from, state) { - state = state || this.state; - this.props.stateListener(from, state && state.color); - }, + TestComponent = class extends React.Component { + constructor(props) { + super(props); + this.peekAtState('getInitialState', undefined, props); + this.state = {color: 'red'}; + } + + peekAtState = (from, state = this.state, props = this.props) => { + props.stateListener(from, state && state.color); + } - peekAtCallback: function(from) { + peekAtCallback = from => { return () => this.peekAtState(from); - }, + } - setFavoriteColor: function(nextColor) { + setFavoriteColor(nextColor) { this.setState( {color: nextColor}, this.peekAtCallback('setFavoriteColor') ); - }, - - getInitialState: function() { - this.peekAtState('getInitialState'); - return {color: 'red'}; - }, + } - render: function() { + render() { this.peekAtState('render'); return
{this.state.color}
; - }, + } - componentWillMount: function() { + componentWillMount() { this.peekAtState('componentWillMount-start'); this.setState(function(state) { this.peekAtState('before-setState-sunrise', state); @@ -71,25 +71,27 @@ describe('ReactCompositeComponent-state', () => { this.peekAtState('after-setState-orange', state); }); this.peekAtState('componentWillMount-end'); - }, + } - componentDidMount: function() { + componentDidMount() { this.peekAtState('componentDidMount-start'); this.setState( {color: 'yellow'}, this.peekAtCallback('setState-yellow') ); this.peekAtState('componentDidMount-end'); - }, + } - componentWillReceiveProps: function(newProps) { + componentWillReceiveProps(newProps) { this.peekAtState('componentWillReceiveProps-start'); if (newProps.nextColor) { this.setState(function(state) { this.peekAtState('before-setState-receiveProps', state); return {color: newProps.nextColor}; }); - this.replaceState({color: undefined}); + // No longer a public API, but we can test that it works internally by + // reaching into the updater. + this.updater.enqueueReplaceState(this, {color: undefined}); this.setState( function(state) { this.peekAtState('before-setState-again-receiveProps', state); @@ -102,28 +104,28 @@ describe('ReactCompositeComponent-state', () => { }); } this.peekAtState('componentWillReceiveProps-end'); - }, + } - shouldComponentUpdate: function(nextProps, nextState) { + shouldComponentUpdate(nextProps, nextState) { this.peekAtState('shouldComponentUpdate-currentState'); this.peekAtState('shouldComponentUpdate-nextState', nextState); return true; - }, + } - componentWillUpdate: function(nextProps, nextState) { + componentWillUpdate(nextProps, nextState) { this.peekAtState('componentWillUpdate-currentState'); this.peekAtState('componentWillUpdate-nextState', nextState); - }, + } - componentDidUpdate: function(prevProps, prevState) { + componentDidUpdate(prevProps, prevState) { this.peekAtState('componentDidUpdate-currentState'); this.peekAtState('componentDidUpdate-prevState', prevState); - }, + } - componentWillUnmount: function() { + componentWillUnmount() { this.peekAtState('componentWillUnmount'); - }, - }); + } + }; }); it('should support setting state', () => { diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactMultiChild-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactMultiChild-test.js index 4fa299e3fda22..4d9bffc425165 100644 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactMultiChild-test.js +++ b/src/renderers/shared/stack/reconciler/__tests__/ReactMultiChild-test.js @@ -33,14 +33,14 @@ describe('ReactMultiChild', () => { var mockUpdate = jest.fn(); var mockUnmount = jest.fn(); - var MockComponent = React.createClass({ - componentDidMount: mockMount, - componentDidUpdate: mockUpdate, - componentWillUnmount: mockUnmount, - render: function() { + class MockComponent extends React.Component { + componentDidMount = mockMount; + componentDidUpdate = mockUpdate; + componentWillUnmount = mockUnmount; + render() { return ; - }, - }); + } + } expect(mockMount.mock.calls.length).toBe(0); expect(mockUpdate.mock.calls.length).toBe(0); @@ -65,13 +65,13 @@ describe('ReactMultiChild', () => { var mockMount = jest.fn(); var mockUnmount = jest.fn(); - var MockComponent = React.createClass({ - componentDidMount: mockMount, - componentWillUnmount: mockUnmount, - render: function() { + class MockComponent extends React.Component { + componentDidMount = mockMount; + componentWillUnmount = mockUnmount; + render() { return ; - }, - }); + } + } expect(mockMount.mock.calls.length).toBe(0); expect(mockUnmount.mock.calls.length).toBe(0); @@ -93,13 +93,13 @@ describe('ReactMultiChild', () => { var mockMount = jest.fn(); var mockUnmount = jest.fn(); - var MockComponent = React.createClass({ - componentDidMount: mockMount, - componentWillUnmount: mockUnmount, - render: function() { + class MockComponent extends React.Component { + componentDidMount = mockMount; + componentWillUnmount = mockUnmount; + render() { return ; - }, - }); + } + } class WrapperComponent extends React.Component { render() { @@ -130,13 +130,13 @@ describe('ReactMultiChild', () => { var mockMount = jest.fn(); var mockUnmount = jest.fn(); - var MockComponent = React.createClass({ - componentDidMount: mockMount, - componentWillUnmount: mockUnmount, - render: function() { + class MockComponent extends React.Component { + componentDidMount = mockMount; + componentWillUnmount = mockUnmount; + render() { return ; - }, - }); + } + } expect(mockMount.mock.calls.length).toBe(0); expect(mockUnmount.mock.calls.length).toBe(0); diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactStateSetters-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactStateSetters-test.js deleted file mode 100644 index c813dfe09c40e..0000000000000 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactStateSetters-test.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright 2013-present, 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 = require('React'); -var ReactStateSetters = require('ReactStateSetters'); -var ReactTestUtils = require('ReactTestUtils'); - -var TestComponent; -var TestComponentWithMixin; - -describe('ReactStateSetters', () => { - beforeEach(() => { - jest.resetModuleRegistry(); - - TestComponent = class extends React.Component { - state = {foo: 'foo'}; - - render() { - return
; - } - }; - - TestComponentWithMixin = React.createClass({ - mixins: [ReactStateSetters.Mixin], - - getInitialState: function() { - return {foo: 'foo'}; - }, - - render: function() { - return
; - }, - }); - }); - - it('createStateSetter should update state', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var setter = ReactStateSetters.createStateSetter( - instance, - function(a, b, c) { - return { - foo: a + b + c, - bar: a * b * c, - }; - } - ); - expect(instance.state).toEqual({foo: 'foo'}); - - setter(1, 2, 3); - expect(instance.state).toEqual({foo: 6, bar: 6}); - - setter(10, 11, 12); - expect(instance.state).toEqual({foo: 33, bar: 1320}); - }); - - it('createStateKeySetter should update state', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var setter = ReactStateSetters.createStateKeySetter(instance, 'foo'); - - expect(instance.state).toEqual({foo: 'foo'}); - - setter('bar'); - expect(instance.state).toEqual({foo: 'bar'}); - - setter('baz'); - expect(instance.state).toEqual({foo: 'baz'}); - }); - - it('createStateKeySetter is memoized', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var foo1 = ReactStateSetters.createStateKeySetter(instance, 'foo'); - var bar1 = ReactStateSetters.createStateKeySetter(instance, 'bar'); - - var foo2 = ReactStateSetters.createStateKeySetter(instance, 'foo'); - var bar2 = ReactStateSetters.createStateKeySetter(instance, 'bar'); - - expect(foo2).toBe(foo1); - expect(bar2).toBe(bar1); - }); - - it('createStateSetter should update state from mixin', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var setter = instance.createStateSetter( - function(a, b, c) { - return { - foo: a + b + c, - bar: a * b * c, - }; - } - ); - expect(instance.state).toEqual({foo: 'foo'}); - - setter(1, 2, 3); - expect(instance.state).toEqual({foo: 6, bar: 6}); - - setter(10, 11, 12); - expect(instance.state).toEqual({foo: 33, bar: 1320}); - }); - - it('createStateKeySetter should update state with mixin', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var setter = instance.createStateKeySetter('foo'); - - expect(instance.state).toEqual({foo: 'foo'}); - - setter('bar'); - expect(instance.state).toEqual({foo: 'bar'}); - - setter('baz'); - expect(instance.state).toEqual({foo: 'baz'}); - }); - - it('createStateKeySetter is memoized with mixin', () => { - var instance = ; - instance = ReactTestUtils.renderIntoDocument(instance); - expect(instance.state).toEqual({foo: 'foo'}); - - var foo1 = instance.createStateKeySetter('foo'); - var bar1 = instance.createStateKeySetter('bar'); - - var foo2 = instance.createStateKeySetter('foo'); - var bar2 = instance.createStateKeySetter('bar'); - - expect(foo2).toBe(foo1); - expect(bar2).toBe(bar1); - }); -}); diff --git a/src/renderers/shared/stack/reconciler/__tests__/ReactUpdates-test.js b/src/renderers/shared/stack/reconciler/__tests__/ReactUpdates-test.js index 044e709b4b382..026a938eb24d2 100644 --- a/src/renderers/shared/stack/reconciler/__tests__/ReactUpdates-test.js +++ b/src/renderers/shared/stack/reconciler/__tests__/ReactUpdates-test.js @@ -391,30 +391,23 @@ describe('ReactUpdates', () => { }, }; - var Box = React.createClass({ - mixins: [UpdateLoggingMixin], - - render: function() { + class Box extends React.Component { + render() { return
{this.props.children}
; - }, - }); - - var Child = React.createClass({ - mixins: [UpdateLoggingMixin], + } + } + Object.assign(Box.prototype, UpdateLoggingMixin); - render: function() { + class Child extends React.Component { + render() { return child; - }, - }); - - var Switcher = React.createClass({ - mixins: [UpdateLoggingMixin], - - getInitialState: function() { - return {tabKey: 'hello'}; - }, + } + } + Object.assign(Child.prototype, UpdateLoggingMixin); - render: function() { + class Switcher extends React.Component { + state = {tabKey: 'hello'}; + render() { var child = this.props.children; return ( @@ -428,20 +421,20 @@ describe('ReactUpdates', () => {
); - }, - }); - - var App = React.createClass({ - mixins: [UpdateLoggingMixin], + } + } + Object.assign(Switcher.prototype, UpdateLoggingMixin); - render: function() { + class App extends React.Component { + render() { return ( ); - }, - }); + } + } + Object.assign(App.prototype, UpdateLoggingMixin); var root = ; root = ReactTestUtils.renderIntoDocument(root); @@ -988,35 +981,6 @@ describe('ReactUpdates', () => { ); }); - it('throws in replaceState if the update callback is not a function', () => { - function Foo() { - this.a = 1; - this.b = 2; - } - var A = React.createClass({ - getInitialState: function() { - return {}; - }, - render: function() { - return
; - }, - }); - var component = ReactTestUtils.renderIntoDocument(); - - expect(() => component.replaceState({}, 'no')).toThrowError( - 'replaceState(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: string.' - ); - expect(() => component.replaceState({}, {})).toThrowError( - 'replaceState(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Object.' - ); - expect(() => component.replaceState({}, new Foo())).toThrowError( - 'replaceState(...): Expected the last optional `callback` argument ' + - 'to be a function. Instead received: Foo (keys: a, b).' - ); - }); - it('throws in forceUpdate if the update callback is not a function', () => { function Foo() { this.a = 1; diff --git a/src/test/__tests__/ReactTestUtils-test.js b/src/test/__tests__/ReactTestUtils-test.js index fa202e7e9d8eb..f63200f6fd741 100644 --- a/src/test/__tests__/ReactTestUtils-test.js +++ b/src/test/__tests__/ReactTestUtils-test.js @@ -91,12 +91,12 @@ describe('ReactTestUtils', () => { it('should have shallow unmounting', () => { var componentWillUnmount = jest.fn(); - var SomeComponent = React.createClass({ - render: function() { + class SomeComponent extends React.Component { + componentWillUnmount = componentWillUnmount; + render() { return
; - }, - componentWillUnmount, - }); + } + } var shallowRenderer = ReactTestUtils.createRenderer(); shallowRenderer.render(); diff --git a/yarn.lock b/yarn.lock index 240e586d2d610..bf09363a81aa6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2060,6 +2060,18 @@ fbjs@^0.8.5: promise "^7.1.1" ua-parser-js "^0.7.9" +fbjs@^0.8.9: + version "0.8.9" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.9.tgz#180247fbd347dcc9004517b904f865400a0c8f14" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -3930,11 +3942,7 @@ object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4257,6 +4265,13 @@ rc@~1.1.6: minimist "^1.2.0" strip-json-comments "~1.0.4" +react-create-class@15.5.0-alpha.2: + version "15.5.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-create-class/-/react-create-class-15.5.0-alpha.2.tgz#b6e55133ecebc95cc0eb0a9a0acf1679a86b02d4" + dependencies: + fbjs "^0.8.9" + object-assign "^4.1.1" + read-only-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" @@ -4585,6 +4600,10 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + sha.js@^2.3.6, sha.js@~2.4.4: version "2.4.8" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"