mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Convert ReactCompositeComponent to createRoot (#28099)
Moves tests depending on legacy APIs to `ReactLegacyCompositeComponents` and updates the rest.
This commit is contained in:
+543
-952
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,800 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactTestUtils;
|
||||
let PropTypes;
|
||||
|
||||
describe('ReactLegacyCompositeComponent', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
PropTypes = require('prop-types');
|
||||
});
|
||||
|
||||
it('should warn about `setState` in render in legacy mode', () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
let renderedState = -1;
|
||||
let renderPasses = 0;
|
||||
|
||||
class Component extends React.Component {
|
||||
state = {value: 0};
|
||||
|
||||
render() {
|
||||
renderPasses++;
|
||||
renderedState = this.state.value;
|
||||
if (this.state.value === 0) {
|
||||
this.setState({value: 1});
|
||||
}
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
let instance;
|
||||
|
||||
expect(() => {
|
||||
instance = ReactDOM.render(<Component />, container);
|
||||
}).toErrorDev(
|
||||
'Cannot update during an existing state transition (such as within ' +
|
||||
'`render`). Render methods should be a pure function of props and state.',
|
||||
);
|
||||
|
||||
// The setState call is queued and then executed as a second pass. This
|
||||
// behavior is undefined though so we're free to change it to suit the
|
||||
// implementation details.
|
||||
expect(renderPasses).toBe(2);
|
||||
expect(renderedState).toBe(1);
|
||||
expect(instance.state.value).toBe(1);
|
||||
|
||||
// Forcing a rerender anywhere will cause the update to happen.
|
||||
const instance2 = ReactDOM.render(<Component prop={123} />, container);
|
||||
expect(instance).toBe(instance2);
|
||||
expect(renderedState).toBe(1);
|
||||
expect(instance2.state.value).toBe(1);
|
||||
|
||||
// Test deduplication; (no additional warnings are expected).
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
ReactDOM.render(<Component prop={123} />, container);
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should pass context to children when not owner', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Child>
|
||||
<Grandchild />
|
||||
</Child>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children);
|
||||
}
|
||||
}
|
||||
|
||||
class Grandchild extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div>{this.context.foo}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const component = ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
expect(ReactDOM.findDOMNode(component).innerHTML).toBe('bar');
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should pass context when re-rendered for static child', () => {
|
||||
let parentInstance = null;
|
||||
let childInstance = null;
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
flag: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
flag: false,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
flag: this.state.flag,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children);
|
||||
}
|
||||
}
|
||||
|
||||
class Middle extends React.Component {
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string,
|
||||
flag: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
childInstance = this;
|
||||
return <span>Child</span>;
|
||||
}
|
||||
}
|
||||
|
||||
parentInstance = ReactTestUtils.renderIntoDocument(
|
||||
<Parent>
|
||||
<Middle>
|
||||
<Child />
|
||||
</Middle>
|
||||
</Parent>,
|
||||
);
|
||||
|
||||
expect(parentInstance.state.flag).toBe(false);
|
||||
expect(childInstance.context).toEqual({foo: 'bar', flag: false});
|
||||
|
||||
parentInstance.setState({flag: true});
|
||||
expect(parentInstance.state.flag).toBe(true);
|
||||
expect(childInstance.context).toEqual({foo: 'bar', flag: true});
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should pass context when re-rendered for static child within a composite component', () => {
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
flag: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
flag: true,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
flag: this.state.flag,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
static contextTypes = {
|
||||
flag: PropTypes.bool,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class Wrapper extends React.Component {
|
||||
parentRef = React.createRef();
|
||||
childRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Parent ref={this.parentRef}>
|
||||
<Child ref={this.childRef} />
|
||||
</Parent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
||||
|
||||
expect(wrapper.parentRef.current.state.flag).toEqual(true);
|
||||
expect(wrapper.childRef.current.context).toEqual({flag: true});
|
||||
|
||||
// We update <Parent /> while <Child /> is still a static prop relative to this update
|
||||
wrapper.parentRef.current.setState({flag: false});
|
||||
|
||||
expect(wrapper.parentRef.current.state.flag).toEqual(false);
|
||||
expect(wrapper.childRef.current.context).toEqual({flag: false});
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should pass context transitively', () => {
|
||||
let childInstance = null;
|
||||
let grandchildInstance = null;
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
depth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Child />;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
depth: this.context.depth + 1,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
childInstance = this;
|
||||
return <Grandchild />;
|
||||
}
|
||||
}
|
||||
|
||||
class Grandchild extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
render() {
|
||||
grandchildInstance = this;
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
|
||||
expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1});
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should pass context when re-rendered', () => {
|
||||
let parentInstance = null;
|
||||
let childInstance = null;
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
state = {
|
||||
flag: false,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
depth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
let output = <Child />;
|
||||
if (!this.state.flag) {
|
||||
output = <span>Child</span>;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string,
|
||||
depth: PropTypes.number,
|
||||
};
|
||||
|
||||
render() {
|
||||
childInstance = this;
|
||||
return <span>Child</span>;
|
||||
}
|
||||
}
|
||||
|
||||
parentInstance = ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
expect(childInstance).toBeNull();
|
||||
|
||||
expect(parentInstance.state.flag).toBe(false);
|
||||
ReactDOM.unstable_batchedUpdates(function () {
|
||||
parentInstance.setState({flag: true});
|
||||
});
|
||||
expect(parentInstance.state.flag).toBe(true);
|
||||
|
||||
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('unmasked context propagates through updates', () => {
|
||||
class Leaf extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span>{this.context.foo}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
class Intermediary extends React.Component {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Leaf />;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: this.props.cntxt,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Intermediary />;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Parent cntxt="noise" />, div);
|
||||
expect(div.children[0].innerHTML).toBe('noise');
|
||||
div.children[0].innerHTML = 'aliens';
|
||||
div.children[0].id = 'aliens';
|
||||
expect(div.children[0].innerHTML).toBe('aliens');
|
||||
expect(div.children[0].id).toBe('aliens');
|
||||
ReactDOM.render(<Parent cntxt="bar" />, div);
|
||||
expect(div.children[0].innerHTML).toBe('bar');
|
||||
expect(div.children[0].id).toBe('aliens');
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext
|
||||
it('should trigger componentWillReceiveProps for context changes', () => {
|
||||
let contextChanges = 0;
|
||||
let propChanges = 0;
|
||||
|
||||
class GrandChild extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span className="grand-child">{this.props.children}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
class ChildWithContext extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="child-with">{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class ChildWithoutContext extends React.Component {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
propChanges++;
|
||||
}
|
||||
|
||||
if (nextContext !== this.context) {
|
||||
contextChanges++;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="child-without">{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
};
|
||||
|
||||
state = {
|
||||
foo: 'abc',
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: this.state.foo,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="parent">{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
|
||||
let parentInstance = null;
|
||||
ReactDOM.render(
|
||||
<Parent ref={inst => (parentInstance = inst)}>
|
||||
<ChildWithoutContext>
|
||||
A1
|
||||
<GrandChild>A2</GrandChild>
|
||||
</ChildWithoutContext>
|
||||
|
||||
<ChildWithContext>
|
||||
B1
|
||||
<GrandChild>B2</GrandChild>
|
||||
</ChildWithContext>
|
||||
</Parent>,
|
||||
div,
|
||||
);
|
||||
|
||||
parentInstance.setState({
|
||||
foo: 'def',
|
||||
});
|
||||
|
||||
expect(propChanges).toBe(0);
|
||||
expect(contextChanges).toBe(3); // ChildWithContext, GrandChild x 2
|
||||
});
|
||||
|
||||
it('only renders once if updated in componentWillReceiveProps in legacy mode', () => {
|
||||
let renders = 0;
|
||||
|
||||
class Component extends React.Component {
|
||||
state = {updated: false};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
expect(props.update).toBe(1);
|
||||
expect(renders).toBe(1);
|
||||
this.setState({updated: true});
|
||||
expect(renders).toBe(1);
|
||||
}
|
||||
|
||||
render() {
|
||||
renders++;
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
const instance = ReactDOM.render(<Component update={0} />, container);
|
||||
expect(renders).toBe(1);
|
||||
expect(instance.state.updated).toBe(false);
|
||||
ReactDOM.render(<Component update={1} />, container);
|
||||
expect(renders).toBe(2);
|
||||
expect(instance.state.updated).toBe(true);
|
||||
});
|
||||
|
||||
it('only renders once if updated in componentWillReceiveProps when batching in legacy mode', () => {
|
||||
let renders = 0;
|
||||
|
||||
class Component extends React.Component {
|
||||
state = {updated: false};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
expect(props.update).toBe(1);
|
||||
expect(renders).toBe(1);
|
||||
this.setState({updated: true});
|
||||
expect(renders).toBe(1);
|
||||
}
|
||||
|
||||
render() {
|
||||
renders++;
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
const instance = ReactDOM.render(<Component update={0} />, container);
|
||||
expect(renders).toBe(1);
|
||||
expect(instance.state.updated).toBe(false);
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
ReactDOM.render(<Component update={1} />, container);
|
||||
});
|
||||
expect(renders).toBe(2);
|
||||
expect(instance.state.updated).toBe(true);
|
||||
});
|
||||
|
||||
it('should update refs if shouldComponentUpdate gives false in legacy mode', () => {
|
||||
class Static extends React.Component {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Component extends React.Component {
|
||||
static0Ref = React.createRef();
|
||||
static1Ref = React.createRef();
|
||||
|
||||
render() {
|
||||
if (this.props.flipped) {
|
||||
return (
|
||||
<div>
|
||||
<Static ref={this.static0Ref} key="B">
|
||||
B (ignored)
|
||||
</Static>
|
||||
<Static ref={this.static1Ref} key="A">
|
||||
A (ignored)
|
||||
</Static>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<Static ref={this.static0Ref} key="A">
|
||||
A
|
||||
</Static>
|
||||
<Static ref={this.static1Ref} key="B">
|
||||
B
|
||||
</Static>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
const comp = ReactDOM.render(<Component flipped={false} />, container);
|
||||
expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('A');
|
||||
expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('B');
|
||||
|
||||
// When flipping the order, the refs should update even though the actual
|
||||
// contents do not
|
||||
ReactDOM.render(<Component flipped={true} />, container);
|
||||
expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('B');
|
||||
expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('A');
|
||||
});
|
||||
|
||||
it('should allow access to findDOMNode in componentWillUnmount in legacy mode', () => {
|
||||
let a = null;
|
||||
let b = null;
|
||||
|
||||
class Component extends React.Component {
|
||||
componentDidMount() {
|
||||
a = ReactDOM.findDOMNode(this);
|
||||
expect(a).not.toBe(null);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
b = ReactDOM.findDOMNode(this);
|
||||
expect(b).not.toBe(null);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
expect(a).toBe(container.firstChild);
|
||||
ReactDOM.render(<Component />, container);
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(a).toBe(b);
|
||||
});
|
||||
|
||||
// @gate !disableLegacyContext || !__DEV__
|
||||
it('context should be passed down from the parent', () => {
|
||||
class Parent extends React.Component {
|
||||
static childContextTypes = {
|
||||
foo: PropTypes.string,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Component extends React.Component {
|
||||
static contextTypes = {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<Parent>
|
||||
<Component />
|
||||
</Parent>,
|
||||
div,
|
||||
);
|
||||
});
|
||||
|
||||
it('should replace state in legacy mode', () => {
|
||||
class Moo extends React.Component {
|
||||
state = {x: 1};
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const moo = ReactTestUtils.renderIntoDocument(<Moo />);
|
||||
// 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);
|
||||
});
|
||||
|
||||
it('should support objects with prototypes as state in legacy mode', () => {
|
||||
const NotActuallyImmutable = function (str) {
|
||||
this.str = str;
|
||||
};
|
||||
NotActuallyImmutable.prototype.amIImmutable = function () {
|
||||
return true;
|
||||
};
|
||||
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 <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const moo = ReactTestUtils.renderIntoDocument(<Moo />);
|
||||
expect(moo.state.str).toBe('first');
|
||||
expect(moo.state.amIImmutable()).toBe(true);
|
||||
|
||||
const secondState = new NotActuallyImmutable('second');
|
||||
moo._replaceState(secondState);
|
||||
expect(moo.state.str).toBe('second');
|
||||
expect(moo.state.amIImmutable()).toBe(true);
|
||||
expect(moo.state).toBe(secondState);
|
||||
|
||||
moo.setState({str: 'third'});
|
||||
expect(moo.state.str).toBe('third');
|
||||
// Here we lose the prototype.
|
||||
expect(moo.state.amIImmutable).toBe(undefined);
|
||||
|
||||
// When more than one state update is enqueued, we have the same behavior
|
||||
const fifthState = new NotActuallyImmutable('fifth');
|
||||
ReactDOM.unstable_batchedUpdates(function () {
|
||||
moo.setState({str: 'fourth'});
|
||||
moo._replaceState(fifthState);
|
||||
});
|
||||
expect(moo.state).toBe(fifthState);
|
||||
|
||||
// When more than one state update is enqueued, we have the same behavior
|
||||
const sixthState = new NotActuallyImmutable('sixth');
|
||||
ReactDOM.unstable_batchedUpdates(function () {
|
||||
moo._replaceState(sixthState);
|
||||
moo.setState({str: 'seventh'});
|
||||
});
|
||||
expect(moo.state.str).toBe('seventh');
|
||||
expect(moo.state.amIImmutable).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not warn about unmounting during unmounting in legacy mode', () => {
|
||||
const container = document.createElement('div');
|
||||
const layer = document.createElement('div');
|
||||
|
||||
class Component extends React.Component {
|
||||
componentDidMount() {
|
||||
ReactDOM.render(<div />, layer);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ReactDOM.unmountComponentAtNode(layer);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class Outer extends React.Component {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Outer>
|
||||
<Component />
|
||||
</Outer>,
|
||||
container,
|
||||
);
|
||||
ReactDOM.render(<Outer />, container);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user