--description--
So far, if any component receives new state
or new props
, it re-renders itself and all its children. This is usually okay. But React provides a lifecycle method you can call when child components receive new state
or props
, and declare specifically if the components should update or not. The method is shouldComponentUpdate()
, and it takes nextProps
and nextState
as parameters.
This method is a useful way to optimize performance. For example, the default behavior is that your component re-renders when it receives new props
, even if the props
haven't changed. You can use shouldComponentUpdate()
to prevent this by comparing the props
. The method must return a boolean
value that tells React whether or not to update the component. You can compare the current props (this.props
) to the next props (nextProps
) to determine if you need to update or not, and return true
or false
accordingly.
--instructions--
The shouldComponentUpdate()
method is added in a component called OnlyEvens
. Currently, this method returns true
so OnlyEvens
re-renders every time it receives new props
. Modify the method so OnlyEvens
updates only if the value
of its new props is even. Click the Add
button and watch the order of events in your browser's console as the lifecycle hooks are triggered.
--hints--
The Controller
component should render the OnlyEvens
component as a child.
assert(
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
return (
mockedComponent.find('Controller').length === 1 &&
mockedComponent.find('OnlyEvens').length === 1
);
})()
);
The shouldComponentUpdate
method should be defined on the OnlyEvens
component.
assert(
(() => {
const child = React.createElement(OnlyEvens)
.type.prototype.shouldComponentUpdate.toString()
.replace(/s/g, '');
return child !== 'undefined';
})()
);
The OnlyEvens
component should return an h1
tag which renders the value of this.props.value
.
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 1000 });
return mockedComponent.find('h1').html();
};
const second = () => {
mockedComponent.setState({ value: 10 });
return mockedComponent.find('h1').html();
};
const firstValue = first();
const secondValue = second();
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
})();
OnlyEvens
should re-render only when nextProps.value
is even.
(() => {
const mockedComponent = Enzyme.mount(React.createElement(Controller));
const first = () => {
mockedComponent.setState({ value: 8 });
return mockedComponent.find('h1').text();
};
const second = () => {
mockedComponent.setState({ value: 7 });
return mockedComponent.find('h1').text();
};
const third = () => {
mockedComponent.setState({ value: 42 });
return mockedComponent.find('h1').text();
};
const firstValue = first();
const secondValue = second();
const thirdValue = third();
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
})();
--seed--
--after-user-code--
ReactDOM.render(<Controller />, document.getElementById('root'));
--seed-contents--
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return true;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
--solutions--
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// Change code below this line
return nextProps.value % 2 === 0;
// Change code above this line
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}