1. Concept
shouldComponentUpdate
is referred to as SCU, which is an important part of performance optimization in React.
shouldComponentUpdate(nextProps, nextState) {<!-- --> // Determine whether it needs to be rendered, if so, return true, otherwise return false if (nextProps.b === this.props.b) {<!-- --> return false; } else {<!-- --> return true; } }
2. Usage scenarios
If one of all child components of the same parent component in react is updated, all remaining child components will be re-rendered, but for the sake of performance, we do not need to update components that have not updated their dependencies. SCU is to solve the problem of re-rendering components whose dependencies have not been updated.
3. SCU actual combat (shallow comparison is applicable to most situations, try not to compare in depth)
1. Basic usage
example 1:
import React from 'react' class App extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> count: 0 } } render() {<!-- --> return <div> <span>{<!-- -->this.state.count}</span> <button onClick={<!-- -->this.onIncrease}>increase</button> </div> } onIncrease = () => {<!-- --> this.setState({<!-- --> count: this.state.count + 1 }) } // Demonstrate the basic use of shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) {<!-- --> if (nextState.count !== this.state.count) {<!-- --> return true // can be rendered } return false // Do not render repeatedly } } export default App
example 1:
import React from 'react' import PropTypes from 'prop-types' class Input extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> title: '' } } render() {<!-- --> return <div> <input value={<!-- -->this.state.title} onChange={<!-- -->this.onTitleChange}/> <button onClick={<!-- -->this.onSubmit}>Submit</button> </div> } onTitleChange = (e) => {<!-- --> this.setState({<!-- --> title:e.target.value }) } onSubmit = () => {<!-- --> const {<!-- --> submitTitle } = this.props submitTitle(this.state.title) // 'abc' this.setState({<!-- --> title: '' }) } } //props type checking Input.propTypes = {<!-- --> submitTitle: PropTypes.func.isRequired } class List extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) } render() {<!-- --> const {<!-- --> list } = this.props return <ul>{<!-- -->list.map((item, index) => {<!-- --> return <li key={<!-- -->item.id}> <span>{<!-- -->item.title}</span> </li> })}</ul> } } //props type checking List.propTypes = {<!-- --> list: PropTypes.arrayOf(PropTypes.object).isRequired } class Footer extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) } render() {<!-- --> return <p> {<!-- -->this.props.text} {<!-- -->this.props.length} </p> } componentDidUpdate() {<!-- --> console.log('footer did update') } shouldComponentUpdate(nextProps, nextState) {<!-- --> console.log(nextProps, this.props) if (nextProps.text !== this.props.text) {<!-- --> // || nextProps.length !== this.props.length return true // can be rendered } return false // Do not render repeatedly } // React default: If the parent component is updated, the child component will also be updated unconditionally! ! ! // Performance optimization is even more important for React! // Does SCU have to be used every time? --Optimize only when necessary } class TodoListDemo extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) //Status (data) promotion this.state = {<!-- --> list: [ {<!-- --> id: 'id-1', title: 'Title 1' }, {<!-- --> id: 'id-2', title: 'Title 2' }, {<!-- --> id: 'id-3', title: 'Title 3' } ], footerInfo: 'Bottom text' } } render() {<!-- --> return <div> <Input submitTitle={<!-- -->this.onSubmitTitle}/> <List list={<!-- -->this.state.list}/> <Footer text={<!-- -->this.state.footerInfo} length={<!-- -->this.state.list.length}/> </div> } onSubmitTitle = (title) => {<!-- --> this.setState({<!-- --> list: this.state.list.concat({<!-- --> id: `id-${<!-- -->Date.now()}`, title }) }) } } export defaultTodoListDemo
Example 3:
import React from 'react' import PropTypes from 'prop-types' import _ from 'lodash' class Input extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> title: '' } } render() {<!-- --> return <div> <input value={<!-- -->this.state.title} onChange={<!-- -->this.onTitleChange}/> <button onClick={<!-- -->this.onSubmit}>Submit</button> </div> } onTitleChange = (e) => {<!-- --> this.setState({<!-- --> title:e.target.value }) } onSubmit = () => {<!-- --> const {<!-- --> submitTitle } = this.props submitTitle(this.state.title) this.setState({<!-- --> title: '' }) } } //props type checking Input.propTypes = {<!-- --> submitTitle: PropTypes.func.isRequired } class List extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) } render() {<!-- --> const {<!-- --> list } = this.props return <ul>{<!-- -->list.map((item, index) => {<!-- --> return <li key={<!-- -->item.id}> <span>{<!-- -->item.title}</span> </li> })}</ul> } componentDidUpdate() {<!-- --> console.log('List did update') } // Add shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) {<!-- --> console.log(nextProps.list, this.props.list) // _.isEqual does deep comparison of objects or arrays (recursively to the end at one time) if (_.isEqual(nextProps.list, this.props.list)) {<!-- --> // If equal, no repeated rendering return false } return true // If not equal, render } } //props type checking List.propTypes = {<!-- --> list: PropTypes.arrayOf(PropTypes.object).isRequired } class TodoListDemo extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> list: [ {<!-- --> id: 'id-1', title: 'Title 1' }, {<!-- --> id: 'id-2', title: 'Title 2' }, {<!-- --> id: 'id-3', title: 'Title 3' } ] } } render() {<!-- --> return <div> <Input submitTitle={<!-- -->this.onSubmitTitle}/> <List list={<!-- -->this.state.list}/> </div> } onSubmitTitle = (title) => {<!-- --> // Correct usage this.setState({<!-- --> list: this.state.list.concat({<!-- --> id: `id-${<!-- -->Date.now()}`, title }) }) // // In order to demonstrate SCU, the wrong usage was deliberately written. SCU must be used with immutable values. // this.state.list.push({ // It has been changed and setState will be the same nextProps === this.props. If SCU returns false, it will not be updated. // id: `id-${Date.now()}`, // title // }) // this.setState({<!-- --> // list: this.state.list // }) } } export default TodoListDemo
2. PureComponent method (class component)
When a class component extends pureComponent, the current component is also a scu of Shallow Comparison
example 1:
import React from 'react' import PropTypes from 'prop-types' class Input extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> title: '' } } render() {<!-- --> return <div> <input value={<!-- -->this.state.title} onChange={<!-- -->this.onTitleChange}/> <button onClick={<!-- -->this.onSubmit}>Submit</button> </div> } onTitleChange = (e) => {<!-- --> this.setState({<!-- --> title:e.target.value }) } onSubmit = () => {<!-- --> const {<!-- --> submitTitle } = this.props submitTitle(this.state.title) this.setState({<!-- --> title: '' }) } } //props type checking Input.propTypes = {<!-- --> submitTitle: PropTypes.func.isRequired } class List extends React.PureComponent {<!-- --> constructor(props) {<!-- --> super(props) } componentDidUpdate() {<!-- --> console.log('List did update') } render() {<!-- --> const {<!-- --> list } = this.props return <ul>{<!-- -->list.map((item, index) => {<!-- --> return <li key={<!-- -->item.id}> <span>{<!-- -->item.title}</span> </li> })}</ul> } // shouldComponentUpdate() {/*Shallow comparison*/} In fact, it is equivalent to adding this to do shallow comparison } //props type checking List.propTypes = {<!-- --> list: PropTypes.arrayOf(PropTypes.object).isRequired } class TodoListDemo extends React.Component {<!-- --> constructor(props) {<!-- --> super(props) this.state = {<!-- --> list: [ {<!-- --> id: 'id-1', title: 'Title 1' }, {<!-- --> id: 'id-2', title: 'Title 2' }, {<!-- --> id: 'id-3', title: 'Title 3' } ] } } render() {<!-- --> return <div> <Input submitTitle={<!-- -->this.onSubmitTitle}/> <List list={<!-- -->this.state.list}/> </div> } onSubmitTitle = (title) => {<!-- --> // Correct usage this.setState({<!-- --> list: this.state.list.concat({<!-- --> id: `id-${<!-- -->Date.now()}`, title }) }) // // Deliberately written incorrect usage in order to demonstrate SCU // this.state.list.push({<!-- --> // id: `id-${Date.now()}`, // title // }) // this.setState({<!-- --> // list: this.state.list // }) } } export default TodoListDemo
3. React.memo high-order component (function component)
The high-order function of React.memo can be called a high-order component
The high-order component React.memo is used for scu, which is also a shallow comparison
scu.
4. Summary of performance optimization
immutable.js