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>
 | |
|     );
 | |
|   }
 | |
| }
 | |
| ```
 |