React中Memo实现指定组件进行渲染

memo可以解决react渲染时的效率问题,react可以将数据渲染在视图中,如果数据没有变化,视图还是重新渲染

import React, { Component } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
class Foo extends Component {
  render() {
    console.log('Foo reander');
    return null;
  }
}

class App extends Component {

  state = {
    count: 0
  };

  render() {
    return (
      <div>
        <p>{this.state.count}</p>
        <button
          onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
          Add
        </button>
        <Foo name="happychen" />
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第1张图片
发现每次点击Foo子组件就会被重新渲染,即使它里面的数据没有发生变化不需要重新渲染,这就可以优化。react中有个生命周期函数shouldComponentUpdate可以控制组件是否重新渲染。

import React, { Component } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends Component {
  render() {
    console.log('Foo reander');
    return null;
  }
}

class App extends Component {

  state = {
    count: 0
  };

  //nextProps是下次渲染用到的props
  shouldComponentUpdate(nextProps, nextState) {
    //将下次渲染用到的props和当前props对比
    if (nextProps.name === this.props.name) {
      return false;//返回false则当前组件不会被重新渲染
    }
    return true;//返回true则当前组件需要被重新渲染
  }

  render() {
    return (
      <div className="app">
        <p>{this.state.count}</p>
        <button
          onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
          Add
        </button>
        <Foo name="happychen" />
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第2张图片
虽然App组件重新渲染了,但在它的子组件Foo中加上shouldComponentUpdate生命周期函数后,Foo组件可以通过name前后值是否发生变化来判断是否重新渲染。
也可以通过react中的PureComponent组件达到同样的效果。

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends PureComponent {
  render() {
    console.log('Foo reander');
    return null;
  }
}

class App extends Component {

  state = {
    count: 0
  };

  render() {
    return (
      <div className="app">
        <p>{this.state.count}</p>
        <button
          onClick={() => { this.setState({ count: this.state.count + 1 }) }}>
          Add
        </button>
        <Foo name="happychen" />
      </div>
    )
  }
}

export default App;

但是PureComponent的实现和使用场景是有局限性的,比如在state中定义一个引用类型的状态。

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends PureComponent {
  render() {
    console.log('Foo reander');
    return <div>子组件person.age的值:{this.props.person.age}</div>;
  }
}

class App extends Component {

  state = {
    count: 0,
    person: {
      age: 18
    }
  };

  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>父组件person.age的值:{person.age}</p>
        <Foo person={person} />
        <button
          onClick={() => {
            person.age++;
            this.setState({
              person
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第3张图片
此时Foo组件本应该重新渲染但并没有重新渲染,是因为PureComponent提供的shouldComponentUpdate发现person的句柄本身没有发生变化才拒绝重新渲染。只有传入的props第一级发生变化才会触发重新渲染。

另外一个陷阱是:下面不改变person属性的值,而是向Foo组件传入一个回调函数。

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends PureComponent {
  render() {
    console.log('Foo reander');
    return <div>子组件person.age的值:{this.props.person.age}</div>;
  }
}

class App extends Component {

  state = {
    count: 0,
    person: {
      age: 18
    }
  };



  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>父组件person.age的值:{person.age}</p>
        <Foo person={person} cb={() => { }} />
        <button
          onClick={() => {
            this.setState({
              count: this.state.count + 1
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第4张图片
这是因为虽然person本身没有发生变化,但每次父组件重新渲染时 传入子组件的内联函数句柄会发生变化,所以Foo组件依然每次重新渲染。

换种回调函数写法:

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends PureComponent {
  render() {
    console.log('Foo reander');
    return <div>子组件person.age的值:{this.props.person.age}</div>;
  }
}

class App extends Component {

  state = {
    count: 0,
    person: {
      age: 18
    }
  };

  callback(){}

  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>父组件person.age的值:{person.age}</p>
        <Foo person={person} cb={this.callback} />
        <button
          onClick={() => {
            this.setState({
              count: this.state.count + 1
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第5张图片
Foo组件没有重新渲染。但这样导致的后果是callback函数中的this指向会发生变化。

接着修改this的指向,这样依然会创建新的函数对象,修改完this的指向后Foo组件还是会每次重新渲染。

接着改:将callback改成类属性

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

class Foo extends PureComponent {
  render() {
    console.log('Foo reander');
    return <div>子组件person.age的值:{this.props.person.age}</div>;
  }
}

class App extends Component {

  state = {
    count: 0,
    person: {
      age: 18
    }
  };

  callback = () => { }

  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>count值:{this.state.count}</p>
        <Foo person={person} cb={this.callback} />
        <button
          onClick={() => {
            this.setState({
              count: this.state.count + 1
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

这样利用箭头函数不会修改this指向,运行发现Foo组件不会每次重新渲染,这样就相对完美了,这是最常见的写法。

将Foo组件改写成函数组件:

import React, { Component, PureComponent } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

function Foo(props){
    console.log('Foo reander');
    return <div>子组件person.age的值:{props.person.age}</div>;
}

class App extends Component{

  state = {
    count: 0,
    person: {
      age: 18
    }
  };

  callback = () => { }

  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>count值:{this.state.count}</p>
        <Foo person={person} cb={this.callback} />
        <button
          onClick={() => {
            this.setState({
              count: this.state.count + 1
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第6张图片
此时PureComponent组件的特性没有用到了,Foo组件会被重新渲染。接着将函数Foo组件用react的memo包裹起来返回新的组件:

import React, { Component,memo } from 'react';
// , PureComponent, memo, useState, useMemo, useCallback
import './App.css';

const Foo = memo(function Foo(props){
    console.log('Foo reander');
    return <div>子组件person.age的值:{props.person.age}</div>;
})

class App extends Component{

  state = {
    count: 0,
    person: {
      age: 18
    }
  };

  callback = () => { }

  render() {
    const person = this.state.person;
    return (
      <div className="app">
        <p>count值:{this.state.count}</p>
        <Foo person={person} cb={this.callback} />
        <button
          onClick={() => {
            this.setState({
              count: this.state.count + 1
            })
          }}>
          Add
        </button>
      </div>
    )
  }
}

export default App;

效果:
React中Memo实现指定组件进行渲染_第7张图片
将Foo组件用react的memo包裹起来,App父组件发生变化时Foo组件没有被重新渲染。

PureComponent为组件提供了对比算法来避免组件做无意义的渲染,减少性能开销。而无状态组件是函数式的,不能继承PureComponent,但可以用memo包裹函数式组件达到同样的效果。

你可能感兴趣的:(react)