diff --git a/test/browser/lifecycle.js b/test/browser/lifecycle.js
index 592f62bfd5..6af82a4064 100644
--- a/test/browser/lifecycle.js
+++ b/test/browser/lifecycle.js
@@ -435,8 +435,11 @@ describe('Lifecycle methods', () => {
// [should not override state with stale values if prevState is spread within getDerivedStateFromProps](https://github.com/facebook/react/blob/25dda90c1ecb0c662ab06e2c80c1ee31e0ae9d36/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js#L1035)
it('should be passed next props and state', () => {
- let nextPropsLog = [];
- let nextStatesLog = [];
+ /** @type {() => void} */
+ let updateState;
+
+ let propLog = [];
+ let stateLog = [];
class Foo extends Component {
constructor(props) {
@@ -444,44 +447,74 @@ describe('Lifecycle methods', () => {
this.state = {
value: 0
};
+ updateState = () => this.setState({
+ value: this.state.value + 1
+ });
}
static getDerivedStateFromProps(props, state) {
- nextPropsLog.push({...props});
- nextStatesLog.push({...state});
+ propLog.push({...props});
+ stateLog.push({...state});
+ // NOTE: Don't do this in real production code!
+ // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
return {
- value: state.value + 1,
+ value: state.value + 1
};
}
- componentDidMount() {
- this.setState({
- value: this.state.value + 1
- });
- }
render() {
- return
{this.state.value}
+ return {this.state.value}
;
}
}
+ // Initial render
+ // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP
let element = render(, scratch);
expect(element.textContent).to.be.equal('1');
- expect(nextStatesLog).to.have.length(1);
-
- element = render(, scratch, scratch.firstChild);
- expect(element.textContent).to.be.equal('3');
+ expect(stateLog).to.have.length(1);
+ expect(stateLog).to.deep.equal([{
+ value: 0
+ }]);
+ expect(propLog).to.deep.equal([{
+ foo: "foo",
+ children: []
+ }]);
- // ...and nextState in shouldComponentUpdate should be
- // the updated state after getDerivedStateFromProps is called...
- expect(nextStatesLog).to.deep.equal([{
+ // New Props
+ // state.value: 1 -> 2 in gDSFP
+ render(, scratch, scratch.firstChild);
+ expect(element.textContent).to.be.equal('2');
+ expect(stateLog).to.deep.equal([{
value: 0
- },{
- value: 2
+ }, {
+ value: 1
+ }]);
+ expect(propLog).to.deep.equal([{
+ foo: "foo",
+ children: []
+ }, {
+ foo: "bar",
+ children: []
}]);
- expect(nextPropsLog).to.deep.equal([{
+ // New state
+ // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP
+ updateState();
+ rerender();
+ expect(element.textContent).to.be.equal('4');
+ expect(stateLog).to.deep.equal([{
+ value: 0
+ }, {
+ value: 1
+ }, {
+ value: 3
+ }]);
+ expect(propLog).to.deep.equal([{
foo: "foo",
children: []
- },{
+ }, {
+ foo: "bar",
+ children: []
+ }, {
foo: "bar",
children: []
}]);
@@ -586,11 +619,14 @@ describe('Lifecycle methods', () => {
});
it('should be passed the previous props and state', () => {
- let previousProps = [];
- let previousStates = [];
+ /** @type {() => void} */
+ let updateState;
+
+ let prevPropsLog = [];
+ let prevStateLog = [];
- let currentProps = [];
- let currentStates = [];
+ let curPropsLog = [];
+ let curStateLog = [];
class Foo extends Component {
constructor(props) {
@@ -598,58 +634,90 @@ describe('Lifecycle methods', () => {
this.state = {
value: 0
};
+ updateState = () => this.setState({
+ value: this.state.value + 1
+ });
}
static getDerivedStateFromProps(props, state) {
+ // NOTE: Don't do this in real production code!
+ // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
return {
- value: state.value + 1,
+ value: state.value + 1
};
}
getSnapshotBeforeUpdate(prevProps, prevState) {
- previousProps.push(prevProps);
- previousStates.push(prevState);
+ prevPropsLog.push({...prevProps});
+ prevStateLog.push({...prevState});
- currentProps.push(this.props);
- currentStates.push(this.state);
- }
- componentDidMount() {
- this.setState({
- value: this.state.value + 1
- });
+ curPropsLog.push({...this.props});
+ curStateLog.push({...this.state});
}
render() {
- return {this.state.value}
+ return {this.state.value}
;
}
}
- let element = render(, scratch);
- expect(element.textContent).to.be.equal('1');
- expect(previousStates).to.have.length(0);
- expect(currentStates).to.have.length(0);
-
- element = render(, scratch, scratch.firstChild);
- expect(element.textContent).to.be.equal('3');
+ // Expectation:
+ // `prevState` in getSnapshotBeforeUpdate should be
+ // the state before setState or getDerivedStateFromProps was called.
+ // `this.state` in getSnapshotBeforeUpdate should be
+ // the updated state after getDerivedStateFromProps was called.
- expect(previousProps).to.deep.equal([{
+ const finalPrevPropsLogs = [{
foo: "foo",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
- // prevState in getSnapshotBeforeUpdate should be
- // the state before getDerivedStateFromProps is called...
- expect(previousStates).to.deep.equal([{
+ const finalPrevStateLogs = [{
value: 1
- }]);
-
- // ...and this.state in getSnapshotBeforeUpdate should be
- // the updated state after getDerivedStateFromProps is called...
- expect(currentStates).to.deep.equal([{
- value: 3
- }]);
+ }, {
+ value: 2
+ }];
- expect(currentProps).to.deep.equal([{
+ const finalCurPropsLogs = [{
foo: "bar",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
+
+ const finalCurStateLogs = [{
+ value: 2
+ }, {
+ value: 4
+ }];
+
+ // Initial render
+ // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP
+ let element = render(, scratch);
+ expect(element.textContent).to.be.equal('1');
+ expect(prevPropsLog).to.have.length(0);
+ expect(prevStateLog).to.have.length(0);
+ expect(curPropsLog).to.have.length(0);
+ expect(curStateLog).to.have.length(0);
+
+ // New props
+ // state.value: 1 -> 2 in gDSFP
+ element = render(, scratch, scratch.firstChild);
+ expect(element.textContent).to.be.equal('2');
+ expect(prevPropsLog).to.deep.equal(finalPrevPropsLogs.slice(0, 1));
+ expect(prevStateLog).to.deep.equal(finalPrevStateLogs.slice(0, 1));
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs.slice(0, 1));
+ expect(curStateLog).to.deep.equal(finalCurStateLogs.slice(0, 1));
+
+ // New state
+ // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP
+ updateState();
+ rerender();
+ expect(element.textContent).to.be.equal('4');
+ expect(prevPropsLog).to.deep.equal(finalPrevPropsLogs);
+ expect(prevStateLog).to.deep.equal(finalPrevStateLogs);
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs);
+ expect(curStateLog).to.deep.equal(finalCurStateLogs);
});
});
@@ -855,11 +923,14 @@ describe('Lifecycle methods', () => {
describe('#componentDidUpdate', () => {
it('should be passed previous props and state', () => {
- let previousProps = [];
- let previousStates = [];
+ /** @type {() => void} */
+ let updateState;
+
+ let prevPropsLog = [];
+ let prevStateLog = [];
- let currentProps = [];
- let currentStates = [];
+ let curPropsLog = [];
+ let curStateLog = [];
class Foo extends Component {
constructor(props) {
@@ -867,58 +938,90 @@ describe('Lifecycle methods', () => {
this.state = {
value: 0
};
+ updateState = () => this.setState({
+ value: this.state.value + 1
+ });
}
static getDerivedStateFromProps(props, state) {
+ // NOTE: Don't do this in real production code!
+ // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
return {
- value: state.value + 1,
+ value: state.value + 1
};
}
componentDidUpdate(prevProps, prevState) {
- previousProps.push(prevProps);
- previousStates.push(prevState);
+ prevPropsLog.push({...prevProps});
+ prevStateLog.push(prevState);
- currentProps.push(this.props);
- currentStates.push(this.state);
- }
- componentDidMount() {
- this.setState({
- value: this.state.value + 1
- });
+ curPropsLog.push({...this.props});
+ curStateLog.push({...this.state});
}
render() {
- return {this.state.value}
+ return {this.state.value}
;
}
}
- let element = render(, scratch);
- expect(element.textContent).to.be.equal('1');
- expect(previousStates).to.have.length(0);
- expect(currentStates).to.have.length(0);
-
- element = render(, scratch, scratch.firstChild);
- expect(element.textContent).to.be.equal('3');
+ // Expectation:
+ // `prevState` in componentDidUpdate should be
+ // the state before setState and getDerivedStateFromProps was called.
+ // `this.state` in componentDidUpdate should be
+ // the updated state after getDerivedStateFromProps was called.
- expect(previousProps).to.deep.equal([{
+ const finalPrevPropsLogs = [{
foo: "foo",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
- // prevState in componentDidUpdate should be
- // the state before getDerivedStateFromProps is called...
- expect(previousStates).to.deep.equal([{
+ const finalPrevStateLogs = [{
value: 1
- }]);
-
- // ...and this.state in componentDidUpdate should be
- // the updated state after getDerivedStateFromProps is called...
- expect(currentStates).to.deep.equal([{
- value: 3
- }]);
+ }, {
+ value: 2
+ }];
- expect(currentProps).to.deep.equal([{
+ const finalCurPropsLogs = [{
foo: "bar",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
+
+ const finalCurStateLogs = [{
+ value: 2
+ }, {
+ value: 4
+ }];
+
+ // Initial render
+ // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP
+ let element = render(, scratch);
+ expect(element.textContent).to.be.equal('1');
+ expect(prevPropsLog).to.have.length(0);
+ expect(prevStateLog).to.have.length(0);
+ expect(curPropsLog).to.have.length(0);
+ expect(curStateLog).to.have.length(0);
+
+ // New props
+ // state.value: 1 -> 2 in gDSFP
+ element = render(, scratch, scratch.firstChild);
+ expect(element.textContent).to.be.equal('2');
+ expect(prevPropsLog).to.deep.equal(finalPrevPropsLogs.slice(0, 1));
+ expect(prevStateLog).to.deep.equal(finalPrevStateLogs.slice(0, 1));
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs.slice(0, 1));
+ expect(curStateLog).to.deep.equal(finalCurStateLogs.slice(0, 1));
+
+ // New state
+ // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP
+ updateState();
+ rerender();
+ expect(element.textContent).to.be.equal('4');
+ expect(prevPropsLog).to.deep.equal(finalPrevPropsLogs);
+ expect(prevStateLog).to.deep.equal(finalPrevStateLogs);
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs);
+ expect(curStateLog).to.deep.equal(finalCurStateLogs);
});
});
@@ -1155,11 +1258,14 @@ describe('Lifecycle methods', () => {
});
it('should be passed next props and state', () => {
- let currentPropsLog = [];
- let currentStatesLog = [];
+ /** @type {() => void} */
+ let updateState;
+
+ let curPropsLog = [];
+ let curStateLog = [];
let nextPropsLog = [];
- let nextStatesLog = [];
+ let nextStateLog = [];
class Foo extends Component {
constructor(props) {
@@ -1167,60 +1273,92 @@ describe('Lifecycle methods', () => {
this.state = {
value: 0
};
+ updateState = () => this.setState({
+ value: this.state.value + 1
+ });
}
static getDerivedStateFromProps(props, state) {
+ // NOTE: Don't do this in real production code!
+ // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
return {
- value: state.value + 1,
+ value: state.value + 1
};
}
shouldComponentUpdate(nextProps, nextState) {
- nextPropsLog.push(nextProps);
- nextStatesLog.push(nextState);
+ nextPropsLog.push({...nextProps});
+ nextStateLog.push({...nextState});
- currentPropsLog.push(this.props);
- currentStatesLog.push(this.state);
+ curPropsLog.push({...this.props});
+ curStateLog.push({...this.state});
return true;
}
- componentDidMount() {
- this.setState({
- value: this.state.value + 1
- });
- }
render() {
- return {this.state.value}
+ return {this.state.value}
;
}
}
- let element = render(, scratch);
- expect(element.textContent).to.be.equal('1');
- expect(nextStatesLog).to.have.length(0);
- expect(currentStatesLog).to.have.length(0);
+ // Expectation:
+ // `this.state` in shouldComponentUpdate should be
+ // the state before setState or getDerivedStateFromProps was called
+ // `nextState` in shouldComponentUpdate should be
+ // the updated state after getDerivedStateFromProps was called
- element = render(, scratch, scratch.firstChild);
- expect(element.textContent).to.be.equal('3');
-
- expect(currentPropsLog).to.deep.equal([{
+ const finalCurPropsLogs = [{
foo: "foo",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
- // this.state in shouldComponentUpdate should be
- // the state before getDerivedStateFromProps is called...
- expect(currentStatesLog).to.deep.equal([{
+ const finalCurStateLogs = [{
value: 1
- }]);
-
- // ...and nextState in shouldComponentUpdate should be
- // the updated state after getDerivedStateFromProps is called...
- expect(nextStatesLog).to.deep.equal([{
- value: 3
- }]);
+ }, {
+ value: 2
+ }];
- expect(nextPropsLog).to.deep.equal([{
+ const finalNextPropsLogs = [{
foo: "bar",
children: []
- }]);
+ }, {
+ foo: "bar",
+ children: []
+ }];
+
+ const finalNextStateLogs = [{
+ value: 2
+ }, {
+ value: 4
+ }];
+
+ // Initial render
+ // state.value: initialized to 0 in constructor, 0 -> 1 in gDSFP
+ let element = render(, scratch);
+ expect(element.textContent).to.be.equal('1');
+ expect(curPropsLog).to.have.length(0);
+ expect(curStateLog).to.have.length(0);
+ expect(nextPropsLog).to.have.length(0);
+ expect(nextStateLog).to.have.length(0);
+
+ // New props
+ // state.value: 1 -> 2 in gDSFP
+ element = render(, scratch, scratch.firstChild);
+ expect(element.textContent).to.be.equal('2');
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs.slice(0, 1));
+ expect(curStateLog).to.deep.equal(finalCurStateLogs.slice(0, 1));
+ expect(nextPropsLog).to.deep.equal(finalNextPropsLogs.slice(0, 1));
+ expect(nextStateLog).to.deep.equal(finalNextStateLogs.slice(0, 1));
+
+ // New state
+ // state.value: 2 -> 3 in updateState, 3 -> 4 in gDSFP
+ updateState();
+ rerender();
+ expect(element.textContent).to.be.equal('4');
+ expect(curPropsLog).to.deep.equal(finalCurPropsLogs);
+ expect(curStateLog).to.deep.equal(finalCurStateLogs);
+ expect(nextPropsLog).to.deep.equal(finalNextPropsLogs);
+ expect(nextStateLog).to.deep.equal(finalNextStateLogs);
});
});