> For the complete documentation index, see [llms.txt](https://yyaozhibo.gitbook.io/qian-duan-xue-xi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yyaozhibo.gitbook.io/qian-duan-xue-xi/react/you-hua-wu-yong-xuan-ran.md).

# 优化无用渲染

## 前述

* `shouldComponentUpdate`
* `Pure Component`
* `React.memo`

## 描述

每当我们设置 React 中某个 state 时，页面都会重新渲染。即使这个 state 的值没有发生改变，也会触发重新渲染，导致页面性能被占用。

## 实践

### 类 - shouldComponentUpdate && PureComponent

普通例子 `TestC`：

```jsx
import React from 'react';

class TestC extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
        
    }
    
    render() {
        return (
            <div >
            {this.state.count}
            <button onClick={()=>this.setState({count: 1})}>Click Me</button>
            </div>
        );
    }
}
export default TestC;

```

{% hint style="warning" %}
执行这个页面可以发现，每次点击按钮，页面都会重绘
{% endhint %}

#### shouldComponentUpdate

用 `shouldComponentUpdate` 重写之前的 `TestC` 组件：

```jsx
import React from 'react';

class TestC extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
    }
    
    // 在 compoenentDidUpdate 中拦截重绘
    shouldComponentUpdate(nextProps, nextState) {
        if (this.state.count === nextState.count) {
            return false
        }
        return true
    }
    
    render() {
        return ( 
            <div> 
            { this.state.count } 
            <button onClick = {
                () => this.setState({ count: 1 }) }> Click Me </button> 
            </div>
        );
    }
}

export default TestC

```

{% hint style="success" %}
由于在 `shouldComponentUpdate` 中进行了拦截，所以当 `state` 没有发生改变的时候，不会触发组件的重绘
{% endhint %}

#### PureComponent

`React` 在 `v15.5` 引入了 `PureComponent`，`React` 在进行组件更新时，如果发现这个 `Component` 是一个 `PureComponent`，会将组件现在的 `state` 和 `props` 和其下一个 `state` 和 `props` 进行浅比较，如果它们的值没有变化，就不会进行更新。

```jsx
import React from 'react'

class TestC extends React.PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }
  
  componentWillUpdate(nextProps, nextState) {
   console.log('componentWillUpdate')
  }
  
  render() {
    return (
      <div>
        { this.state.count }
        <button onClick = {
            () => this.setState({ count: 1 })
        }> Click Me </button>
      </div>
    )
  }
}

export default TestC

```

{% hint style="success" %}
可以看到上面 `PureComponent` 实现了和我们在 `shouldComponentUpdate` 中实现的功能一样。

但是，我曾遇到一个坑，如果 `state` 树深度超过 2 可能会出现无论如何改变 `state` 都无法触发重绘函数
{% endhint %}

### 函数组件 - React.memo

函数组件和类不同，没有 `shouldComponentUpdate` 这些属性

普通例子 `TestC`：

```jsx
import React from 'react';

const TestC = (props) => {
    console.log(`Rendering TestC :` props)
    return ( 
        <div>
            {props.count}
        </div>
    )
}
export default TestC;
// App.js
<TestC count={5} />

```

#### React.memo

`React.memo` 是 `React v16.6` 引入的新特性，它的作用和`React.PureComponent`类似，是用来控制函数组件的重新渲染的。`React.memo(...)` 其实就是函数组件的`React.PureComponent`。

```jsx
import React from 'react';

let TestC = (props) => {
    console.log('Rendering TestC :', props)
    return ( 
        <div>
        { props.count }
        </>
    )
}
TestC = React.memo(TestC);

export default TestC;

```

{% hint style="success" %}
此时，当我们传入 `props.count` 没有发生改变时，便不会再重绘
{% endhint %}

## 结论

* `React.PureComponent`是给ES6的类组件使用的
* `React.memo(...)`是给函数组件使用的
* `React.PureComponent`减少ES6的类组件的无用渲染
* `React.memo(...)`减少函数组件的无用渲染
