React 高阶组件应用(一) 改善 antd Table组件的渲染效率

一、前言

对于react 高阶组件。大家基本只知道定义和用法,在实际开发中,使用的可能并不多。项目组下来一个任务,重写某个由Antd Table渲染为主要功能的模块,这里发现一些问题,Antd Table在渲染时,即使没有改变Tableprops,只是改变包裹Table的组件,Table组件也会重新渲染。

二、问题产生

import {
    
     Table} from 'antd';
class TestRoot extends Component {
    
    
	change() {
    
    
	 		this.setState({
    
    
	            self_state: 2
	        });
	}
	render() {
    
    
	        const columns = [
	            {
    
    
	                title: '姓名',
	                dataIndex: 'name',
	                key: 'name',
	                render: (text, record, index) => {
    
    
	                    console.log('rerendered !!!')
	                    return <div>{
    
    text}</div>;
	                }
	            },
	            {
    
    
	                title: '年龄',
	                dataIndex: 'age',
	                key: 'age'
	            },
	            {
    
    
	                title: '住址',
	                dataIndex: 'address',
	                key: 'address'
	            }
	        ];
	        return (
	            <div>
	                <h1>{
    
    this.state.self_state}</h1>
	                <Table columns={
    
    columns} dataSource={
    
    this.state.data_arr} />
	            </div>
	        );
	    }
 }

当我的TestRoot组件自身状态发生改变时,会发现控制台不断的输出rerendered !!!这样的提示。证明Table表格内部做了重新渲染操作。

三、分析 & 解决

Antd 这样的UI库,重复渲染问题是优化问题,一般来说库不会太过分做这些优化。我们要做的就是通过控制Table组件的shouldComponentUpdate来进行过滤非必要的状态渲染。这时候我们想到了利用高阶组件封装,采用反向继承的方式,注入到Table组件的渲染逻辑中:

import {
    
     Table } from 'antd';
const withPure = (Comp) => {
    
    
    return class PureTable extends Comp {
    
    
        shouldComponentUpdate(nextProps, nextState) {
    
    
            //--在这里控制
            return true;
        }
        render() {
    
    
            return super.render();
        }
    };
};
export default withPure(Table)

使用方式,将原先直接使用Table换成使用PureTable

 return (
          <div>
               <h1>{
    
    this.state.self_state}</h1>
               <PureTable columns={
    
    columns} dataSource={
    
    this.state.data_arr} />
           </div>
       );

大家注意,这是高阶组件的第二种实现方式。采用的是继承源组件的形式,这样我们可以通过覆写源组件的一些方法,来实现控制渲染流程,是一种深度攻击注入。

1. 通过shallowCompare 进行第一层浅比较

有关shallowCompare 的用法,大家可以自行百度,就是一层浅比较。可以判断出state和props第一层的变化。但是无法判断深层次的引用对象。

npm i react-addons-shallow-compare --save

import {
    
     Table } from 'antd';
let shallowCompare = require('react-addons-shallow-compare');
const withPure = (Comp) => {
    
    
    return class PureTable extends Comp {
    
    
        shouldComponentUpdate(nextProps, nextState) {
    
    
           return !shallowCompare(this, nextProps, nextState);
        }
        render() {
    
    
            return super.render();
        }
    };
};
export default withPure(Table)

2.通过lodash _.isEqual进行深比较

上述代码测试后发现,改变self_state后,确实表格没有再渲染。但是很明显,当我的dataSource进行变化时,shallowCompare并不会判断出来,禁止了表格的渲染。例如下面这个直接修改dataSource的例子:

change3() {
    
    
    const {
    
     data_arr } = this.state;

        data_arr[0].name = '胡二狗';

        this.setState({
    
    
            data_arr
        });
}

这里大家有2个分支,第一个是根据业务情况,自行判断需要的Table的深props和state,比如dataSource属性,或者直接使用深比较,判断状态和props。第二种方式明显效率更低,但是可以保证表格的渲染不会受到影响。


import {
    
     Table } from 'antd';
let shallowCompare = require('react-addons-shallow-compare');
const withPure = (Comp) => {
    
    
    return class PureTable extends Comp {
    
    
        shouldComponentUpdate(nextProps, nextState) {
    
    
           const is_sample = shallowCompare(this, nextProps, nextState);

           if(!is_sample) {
    
    return true;}

           const state_sample = _.isEqual(nextState, this.state);
           if(!state_sample) {
    
    return true;}

           
           const props_sample = _.isEqual(nextProps, this.props);
           if(!props_sample) {
    
    return true;}
           
           return false;
        }
        render() {
    
    
            return super.render();
        }
    };
};
export default withPure(Table)

到这里,理论上当我只改变Root组件的自身状态时,不会在触发表格的渲染了。可是在lodash进行深比较时,还是有一个属性判断不一致,深度调试之后发现是两次props传递的columns属性中的render函数不同。这里发现一个表格写法问题。

 const columns = [
         {
    
    
               title: '姓名',
               dataIndex: 'name',
               key: 'name',
               render: (text, record, index) => {
    
    
                   console.log('rerendered !!!')
                   return <div>{
    
    text}</div>;
               }
           },
           {
    
    
               title: '年龄',
               dataIndex: 'age',
               key: 'age'
           },
           {
    
    
               title: '住址',
               dataIndex: 'address',
               key: 'address'
           }
       ];
       return (
           <div>
                <h1>{
    
    this.state.self_state}</h1>
                <Table columns={
    
    columns} dataSource={
    
    this.state.data_arr} />
            </div>
        );

大家注意姓名那一栏的render函数。这种写法会导致每次都创建一个新的render函数引用,导致lodash深比较的时候,判断不一致,导致表格重新渲染。

将render方法定义在类中,修改定义方式如下:

//--省略
    render_column_name = (text, record, index) => {
    
    
        console.log('column rendered!');
        return <TestColumnChild name={
    
    text} ></TestColumnChild>;
    }
    render() {
    
    
        const columns = [
            {
    
    
                title: '姓名',
                dataIndex: 'name',
                key: 'name',
                render: this.render_column_name
            },
            {
    
    
                title: '年龄',
                dataIndex: 'age',
                key: 'age'
            },
            {
    
    
                title: '住址',
                dataIndex: 'address',
                key: 'address'
            }
        ];
   )

经过测试,一切正常。设置Root本身的状态不会再导致表格的重新渲染。

四、升华

其实我们发现,我们只是通过包装器,自己实现了一个pureComponent。这个包装器,如果按照上面深浅比较的通用写法,其实可以包装任何组件,不仅仅限于Table。其实这就是高阶组件的精髓所在。这里引入一个库,recompose 这是react中的lodash。这里仅仅抛砖引玉,给出我上面的写法的一种recompose实现。后续章节会就recompose做更详细的分享。

import {
    
     compose, pure } from 'recompose';
import {
    
     Table} from 'antd';

const composeHoc = compose(pure);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
export default  composeHoc(Table);

是不是很简单?

这里的实现方式,就是通过recompose原生的pure包装器去进行组件包装。只不过这里的pure是浅比较。recompose内提供了大量的短小精美的包装器,可以通过compose进行层层包装,增强我们的组件。

猜你喜欢

转载自blog.csdn.net/qq_29722281/article/details/107518148