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