React新特性-memo(三)

父组件进行了setState,众所周知子组件都会进行重新渲染。React.memo会做浅层对比传入的props,如果传入的props没有改变,这个子组件就不会重新渲染。

React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。

系列目录

React性能优化之shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)是React一个生命周期函数,这个函数需要返回一个Boolean值来判断是否要重新渲染。

propsstate 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用,返回值默认为 true

此方法仅作为性能优化的方式而存在

下面示例:

//App.js
import React, { Component } from 'react';
import TestButton from './TestButton';

export default class App extends Component {
  state = {
    inputValue: '',
    num:1,
  }
  render() {
    return (
      <div className="App">
        <input
          value={this.state.inputValue}
          onChange={(e)=>this.setState({inputValue:e.target.value})}
        />
        <TestButton num={this.state.num}/>
      </div>
    )
  }
}

//TestButton.js
import React, { Component } from 'react'
export default class TestButton extends Component{
  render(){
    console.log('我是TestButton组件,我被渲染啦');
    return (
      <div>
        数字:{this.props.num}
        <button>提交+1</button>
      </div>
    )
  }
}

从上面代码中可以看出TestButtonApp组件的子组件。App组件所作用的事情就是使input输入框受控。

从上面的图片可以看出,当页面加载的时候App渲染一次,然后TestButton渲染一次。

当input输入变更state的时候,App在不停的重新渲染得到界面更新。但是TestButton也在不停的渲染,TestButton完全没有必要渲染,因为他的stateprops都未曾改变。这时候就可以进行性能优化,去掉不必要的渲染。

如下:

//App.js
import React, { Component } from 'react';
import TestButton from './TestButton';

export default class App extends Component {
  state = {
    inputValue: '',
    num:1,
  }
  render() {
    return (
      <div className="App">
        <input
          value={this.state.inputValue}
          onChange={(e)=>this.setState({inputValue:e.target.value})}
        />
        <TestButton num={this.state.num} onClick={()=>{
          this.setState((prevState)=>({num:prevState.num+1}))
        }}/>
      </div>
    )
  }
}

//TestButton.js
import React, { Component } from 'react'
export default class TestButton extends Component{
  //手动对比是否要重新渲染
  shouldComponentUpdate(props){
    if(props.num !== this.props.num){
      return true
    }
    return false
  }
  render(){
    console.log('我TestButton组件,我被渲染啦');
    return (
      <div>
        数字:{this.props.num}
        <button onClick={this.props.onClick}>提交+1</button>
      </div>
    )
  }
}

这次我们在TestButton组件中加入了shouldComponentUpdate生命周期函数来手动判断是否要重新渲染组件来达到性能优化

从图二中可以看出input怎么输入TestButton都不为所动,只有当num的值进行了state变更才会重新渲染TestButton组件

React性能优化之React.PureComponent

每次在组件中使用shouldComponentUpdate就有点麻烦,有没有自动对比数据自动判断是否要更新呢? 当然有,React提供了React.PureComponent来做这件事情

React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别

我们来看是示例:

//仅仅修改TestButton.js
import React, { PureComponent } from 'react'
//继承PureComponent组件来做自动props对比
export default class TestButton extends PureComponent{
  render(){
    console.log('我TestButton组件,我被渲染啦');
    return (
      <div>
        数字:{this.props.num}
        <button onClick={this.props.onClick}>提交+1</button>
      </div>
    )
  }
}

可以看出 TestButton 继承了PureComponent组件,然后我们在输入试试。这时候发现input输入还是会引起TestButton渲染,这是为什么呢?答案是:

//App.js
//当组件重新渲染onClick事件每次就会生成新的函数
<TestButton num={this.state.num} onClick={()=>{
  this.setState((prevState)=>({num:prevState.num+1}))
}}/>

由于每次输入造成重新渲染,而onClick事件中的回调函数每次也会重新生成函数,导致传入的propsonClick的值(引用地址)在不停的变更新。

这样PureComponent在做对比的时候就认为props每次接受到的都是一个新的值而不得不去更新组件。

想要解决我们把代码改成如下这样:

import React, { Component } from 'react';
import TestButton from './TestButton';

export default class App extends Component {
  state = {
    inputValue: '',
    num:1,
  }

  //App.prototype.onClick=()=>xxxxxx 每次重新渲染onClick得到的函数地址始终是不变的
  onClick=()=>{
    this.setState((prevState)=>({num:prevState.num+1}))
  }
  render() {
    return (
      <div className="App">
        <input
          value={this.state.inputValue}
          onChange={(e)=>this.setState({inputValue:e.target.value})}
        />
        <TestButton num={this.state.num} onClick={this.onClick}/>
      </div>
    )
  }
}

每次重新渲染onClick得到的函数地址始终是不变的,这样就解决了TestButton重新渲染的问题

React性能优化之React.memo

讲了这么多,下面请出React.memo()函数,其实上面已经都把memo的工作原理全部展现出来了。

按照React官方的说法TestButton组件是一个无状态组件,可以用函数组件来代替class组件来提高优化。

示例:

import React, { memo } from 'react'
const TestButton = (props) => {
  console.log('我TestButton组件,我被渲染啦');
  return (
    <div>
      数字:{props.num}
      <button onClick={props.onClick}>提交+1</button>
    </div>
  )
}
//memo是高阶组件
export default memo(TestButton)

因为是函数组件既无生命周期函数shouldComponentUpdate可用,也不能继承PureComponent,所以React提供了memo使用来达到一样的效果,其使用原理都和上面一样。

上面例子,用memo这个高阶组件包裹TestButton函数组件,当父组件输入setState时候子组件memo自动判断num是否有变化,如果没有就不会重新渲染TestButton组件

拆分细的组件,使用到的优化机会就越多。

function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);

memo也支持手动对比,类似shouldComponentUpdate函数一样来判断


文章作者: jackie chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jackie chen !
评论
 上一篇
React新特性-Hooks之useState(四) React新特性-Hooks之useState(四)
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 系列目录 React新特性-Context(一) React新特性-Lazy和Suspense(二
2019-07-12
下一篇 
React新特性-Lazy和Suspense(二) React新特性-Lazy和Suspense(二)
系列目录 React新特性-Context(一) React新特性-Lazy和Suspense(二) React新特性-memo(三) React新特性-Hooks之useState(四) React新特性-Hooks之useEffect和
2019-07-09
  目录