188 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			188 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						||
| 
								 | 
							
								id: 5a24c314108439a4d4036180
							 | 
						||
| 
								 | 
							
								title: Optimize Re-Renders with shouldComponentUpdate
							 | 
						||
| 
								 | 
							
								challengeType: 6
							 | 
						||
| 
								 | 
							
								forumTopicId: 301398
							 | 
						||
| 
								 | 
							
								dashedName: optimize-re-renders-with-shouldcomponentupdate
							 | 
						||
| 
								 | 
							
								---
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# --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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								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`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								(() => {
							 | 
						||
| 
								 | 
							
								  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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								(() => {
							 | 
						||
| 
								 | 
							
								  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--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```jsx
							 | 
						||
| 
								 | 
							
								ReactDOM.render(<Controller />, document.getElementById('root'));
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								## --seed-contents--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```jsx
							 | 
						||
| 
								 | 
							
								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--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```jsx
							 | 
						||
| 
								 | 
							
								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>
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								```
							 |