[React] higher-order learning component summary

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/Weixiaohuai/article/details/90484564

 

I. Introduction

Is a high-order component can be abstracted in a way more components of common functions, higher-order component is actually a function that takes a general assembly as an argument, then we have gone through some processing package, return this package is an intermediate components, like decorative pattern in JAVA.

There are two main components of higher order implementations:

. a property agent (Props Proxy): high-order components is performed by the operations related props wrappedComponent;
B inherit inverted (Inheritance Inversion):. inherited from higher-order component wrappedComponent;

Here we were two ways to make a brief introduction.

Second, the property broker

The main methods of property broker function the following points:

[A] Operating props: props can be carried out on the original components CRUD usually find and add, delete and modify it, taking into account the need not destroy the original components;

[B] refs access component by component instance and calling methods: ref = {componentInstance => this.componentInstance = componentInstance};

[C] extracting state: onClickListener following example, count the callback to the general assembly of simple counting function;

[D] with another element wrapped WrappedComponent, implement the layout purposes: to form an outermost layer added as scroll bars;

The following four functions described above by an example:

MyApp.js:

import React from 'react';
import {Card} from 'antd';
import {wrappedComponent} from './WrapComponent';

/**
 * @Description: 普通组件
 * @author weixiaohuai
 * @date 2019/5/15 15:15
 */
class MyApp extends React.Component {

    componentDidMount() {
        //基础组件中访问高阶组件扩展的属性
        const {name, age, count} = this.props;
        console.log('name', name);
        console.log('age', age);
        console.log('count', count);
    }

    sayHello = () => {
        console.log('使用ref获取组件实例,调用组件方法');
    };

    render() {
        const {onClickListener, count} = this.props;
        return (
            <div>
                <Card title="高阶组件(属性代理(Props Proxy))的简单示例" extra={<a onClick={onClickListener}>点击</a>}
                      style={{width: 300}}>
                    <p>一共点击了{count}次</p>
                </Card>
            </div>
        );
    }
}

//返回高阶函数处理之后的一个实例,其实就是高阶组件中定义的中间组件
export default wrappedComponent(MyApp);




WrapComponent.js:

import React from 'react';
import {Alert} from 'antd';

/**
 * 高阶函数: 将普通组件作为参数,然后经过一些逻辑处理后返回一个新的组件
 *
 * @param Component 普通组件
 * 基础组件作为高阶组件的参数传入
 *
 * 说明: 高阶组件是一个实现抽象组件公共功能的好方法。高阶组件其实是一个函数,接收一个组件作为参数,
 返回一个包装组件作为返回值,类似于高阶函数。高阶组件和装饰器就是一个模式,因此,高阶组件可以作为
 装饰器来使用
 *
 * 属性代理(Props Proxy)
 1. 操作props: 可以对原组件的props进行增删改查,通常是查找和增加,删除和修改的话,需要考虑到不能破坏原组件;
 2. 通过refs访问组件实例并调用组件的方法: ref={componentInstance => this.componentInstance = componentInstance};
 3. 提取state: 如下示例的onClickListener、count回调给普通组件实现简单计数功能;
 4. 用其他元素包裹WrappedComponent, 实现布局等目的: 如给表格最外层加入滚动条等;
 */
export const wrappedComponent = (Component) => {
    //创建一个中间组件,该中间组件会在添加了逻辑之后返回
    return class MyApp2 extends React.Component {

        constructor(props) {
            super(props);
            this.state = {
                name: 'weixiaohuai',
                age: 20,
                count: 0
            }
        }

        componentDidMount() {
            let currentComponentInstance = this.componentInstance;
            //获取当前组件实例
            console.log('当前组件实例--> ', currentComponentInstance);

            //调用当前组件的方法
            const {sayHello} = currentComponentInstance;
            if (sayHello && typeof sayHello === 'function') {
                sayHello();
            }
        }

        onClickListener = () => {
            const {count} = this.state;
            this.setState({
                count: count + 1
            });
        };

        render() {
            return (
                <div>
                    {/*高阶组件为基础组件中扩展新的props*/}
                    <Component {...this.props} {...this.state} onClickListener={this.onClickListener}
                               ref={componentInstance => this.componentInstance = componentInstance}>高阶组件</Component>
                    {/*使用div包裹WrappedComponent,扩展一些布局组件、样式等*/}
                    <Alert message="Success Text" type="success"/>
                </div>
            )
        }
    }
};



 

Third, the reverse inheritance

The main methods of inheritance reverse features the following points:

[A] to render hijacking: hijack inherited class may render the content, modify, filtered, and returns the new display content, for example to form a dynamically increasing / add components to the original data and the like

[B] through inheritance WrappedComponent, we can get some static methods in addition to, including life cycle, state, and other various function (this.state / this.props), as an example WrappedComponent3.js

[C] the rendering conditions: Common components can hijack content rendered in the current, the control returns dynamic content / or parcel then return to normal assembly; example as WrappedComponent4.js

The following examples illustrate a few more functions are:

. A hijacking Rendering: add some data to the original module, and then render the new data;

import React from 'react';

/**
 *
 * @param Component 普通组件
 * 基础组件作为高阶组件的参数传入
 *
 *  继承反转(Inheritance Inversion)
 *
 *  1. 渲染劫持: 可以劫持被继承class的render内容,进行修改,过滤后,返回新的显示内容, 例如给表格动态增加一列/给原组件添加一些数据等等
 *  2. 通过继承WrappedComponent,我们可以获取除了一些静态方法,包括生命周期,state,各种function等 (this.state/this.props),示例如WrappedComponent3.js
 *  3. 条件渲染: 可以劫持当前渲染的普通组件中的内容,动态控制返回内容/或者包裹普通组件再进行返回; 示例如WrappedComponent4.js
 */
export const wrappedComponent = (Component) => {
    return class extends Component {

        render() {
            //为原组件中添加一些数据,然后渲染新的数据
            const elementTree = super.render();
            console.log('MyApp2 ---elementTree', elementTree);
            const {children} = elementTree.props;
            console.log('children', children);
            let {dataSource} = children.props;

            //[{title: "Ant Design Title 1"},{title: "Ant Design Title 2"},{title: "Ant Design Title 3"},{title: "Ant Design Title 4"}]
            console.log('dataSource', dataSource);
            dataSource.push({
                title: 'Ant Design Title 5',
            });
            dataSource.push({
                title: 'Ant Design Title 6',
            });
            //克隆组件,然后返回
            return React.cloneElement(elementTree);
        }
    }
};



import React from 'react';
import {wrappedComponent} from './WrapComponent2';
import {Avatar, List} from 'antd';

/**
 * @Description:
 * @author weishihuai
 * @date 2019/5/16 11:28
 */
@wrappedComponent
class MyApp2 extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            name: 'weixiaohuai'
        }
    }

    componentDidMount() {
        console.log('componentDidMount');
    }

    render() {
        const data = [
            {
                title: 'Ant Design Title 1',
            },
            {
                title: 'Ant Design Title 2',
            },
            {
                title: 'Ant Design Title 3',
            },
            {
                title: 'Ant Design Title 4',
            },
        ];


        return (
            <div>
                <List
                    itemLayout="horizontal"
                    dataSource={data}
                    renderItem={item => (
                        <List.Item>
                            <List.Item.Meta
                                avatar={<Avatar
                                    src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"/>}
                                title={<a href="https://ant.design">{item.title}</a>}
                                description="Ant Design, a design language for background applications, is refined by Ant UED Team"
                            />
                        </List.Item>
                    )}
                />
            </div>
        );
    }
}

export default MyApp2;




. B accessing a life cycle, state, and other various function (this.state / this.props);

WrapComponent3.js:

import React from 'react';

export const wrappedComponent = (Component) => {
    return class extends Component {

        render() {
            console.log('this.props', this.props);
            console.log('this.state', this.state);
            return super.render();
        }
    }
};
import React from 'react';
import {wrappedComponent as wrappedComponent3} from './WrapComponent3'
import {Avatar, List} from 'antd';

/**
 * @Description:
 * @author weishihuai
 * @date 2019/5/16 11:28
 */
@wrappedComponent3
class MyApp2 extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            name: 'weixiaohuai'
        }
    }

    componentDidMount() {
        console.log('componentDidMount');
    }

    render() {
        const data = [
            {
                title: 'Ant Design Title 1',
            },
            {
                title: 'Ant Design Title 2',
            },
            {
                title: 'Ant Design Title 3',
            },
            {
                title: 'Ant Design Title 4',
            },
        ];


        return (
            <div>
                <List
                    itemLayout="horizontal"
                    dataSource={data}
                    renderItem={item => (
                        <List.Item>
                            <List.Item.Meta
                                avatar={<Avatar
                                    src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"/>}
                                title={<a href="https://ant.design">{item.title}</a>}
                                description="Ant Design, a design language for background applications, is refined by Ant UED Team"
                            />
                        </List.Item>
                    )}
                />
            </div>
        );
    }
}

export default MyApp2;




 

. C rendering conditions: Common components can hijack content rendered in the current, the control returns dynamic content / normal assembly or parcel then returned;

WrapComponent4.js:

import React from 'react';

export const wrappedComponent = isShow => (Component) => {
    return class extends Component {
        render() {
            return isShow ? super.render() : '暂无内容';
        }
    }
};



import React from 'react';
import {wrappedComponent as wrappedComponent4} from './WrapComponent4'
import {Avatar, List} from 'antd';

@wrappedComponent4(false)
class MyApp2 extends React.Component {
    
    render() {
        const data = [
            {
                title: 'Ant Design Title 1',
            },
            {
                title: 'Ant Design Title 2',
            },
            {
                title: 'Ant Design Title 3',
            },
            {
                title: 'Ant Design Title 4',
            },
        ];


        return (
            <div>
                <List
                    itemLayout="horizontal"
                    dataSource={data}
                    renderItem={item => (
                        <List.Item>
                            <List.Item.Meta
                                avatar={<Avatar
                                    src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"/>}
                                title={<a href="https://ant.design">{item.title}</a>}
                                description="Ant Design, a design language for background applications, is refined by Ant UED Team"
                            />
                        </List.Item>
                    )}
                />
            </div>
        );
    }
}

export default MyApp2;




 

Fourth, the high-order component parameter passing mode

Directly on a case to explain how to pass parameters higher order components:

MyTable01.js:

import React from 'react';
import {Divider, Table, Tag} from 'antd';
import {wrappedComponent} from "./WrapComponent";

@wrappedComponent(true, false, undefined)
class MyTable01 extends React.Component {

    render() {
        const columns = [
            {
                title: 'Name',
                dataIndex: 'name',
                key: 'name',
                render: text => <a href="javascript:;">{text}</a>,
            },
            {
                title: 'Age',
                dataIndex: 'age',
                key: 'age',
            },
            {
                title: 'Address',
                dataIndex: 'address',
                key: 'address',
            },
            {
                title: 'Tags',
                key: 'tags',
                dataIndex: 'tags',
                render: tags => (
                    <span>
                        {tags.map(tag => {
                            let color = tag.length > 5 ? 'geekblue' : 'green';
                            if (tag === 'loser') {
                                color = 'volcano';
                            }
                            return (
                                <Tag color={color} key={tag}>
                                    {tag.toUpperCase()}
                                </Tag>
                            );
                        })}
                    </span>
                ),
            },
            {
                title: 'Action',
                key: 'action',
                render: (text, record) => (
                    <span>
                        <a href="javascript:;">Invite {record.name}</a>
                        <Divider type="vertical"/>
                        <a href="javascript:;">Delete</a>
                    </span>
                ),
            },
        ];

        const data = [
            {
                key: '1',
                name: 'John Brown',
                age: 32,
                address: 'New York No. 1 Lake Park',
                tags: ['nice', 'developer'],
            },
            {
                key: '2',
                name: 'Jim Green',
                age: 42,
                address: 'London No. 1 Lake Park',
                tags: ['loser'],
            },
            {
                key: '3',
                name: 'Joe Black',
                age: 32,
                address: 'Sidney No. 1 Lake Park',
                tags: ['cool', 'teacher'],
            },
        ];

        return (
            <div>
                <Table columns={columns} dataSource={data} {...this.props}/>
            </div>
        );
    }
}

export default MyTable01;




MyTable02.js:

import React from 'react';
import {Button, Divider, Table, Tag} from 'antd';
import {wrappedComponent} from "./WrapComponent";

const {Column, ColumnGroup} = Table;

@wrappedComponent(true, true, () => <div>Here is footer <Button>xxx</Button></div>)
class MyTable02 extends React.Component {

    render() {
        const data = [
            {
                key: '1',
                firstName: 'John',
                lastName: 'Brown',
                age: 32,
                address: 'New York No. 1 Lake Park',
                tags: ['nice', 'developer'],
            },
            {
                key: '2',
                firstName: 'Jim',
                lastName: 'Green',
                age: 42,
                address: 'London No. 1 Lake Park',
                tags: ['loser'],
            },
            {
                key: '3',
                firstName: 'Joe',
                lastName: 'Black',
                age: 32,
                address: 'Sidney No. 1 Lake Park',
                tags: ['cool', 'teacher'],
            },
        ];


        return (
            <div>
                <Table dataSource={data} {...this.props}>
                    <ColumnGroup title="Name">
                        <Column title="First Name" dataIndex="firstName" key="firstName"/>
                        <Column title="Last Name" dataIndex="lastName" key="lastName"/>
                    </ColumnGroup>
                    <Column title="Age" dataIndex="age" key="age"/>
                    <Column title="Address" dataIndex="address" key="address"/>
                    <Column
                        title="Tags"
                        dataIndex="tags"
                        key="tags"
                        render={tags => (
                            <span>
                                  {tags.map(tag => (
                                      <Tag color="blue" key={tag}>
                                          {tag}
                                      </Tag>
                                  ))}
                            </span>
                        )}
                    />
                    <Column
                        title="Action"
                        key="action"
                        render={(text, record) => (
                            <span>
                              <a href="javascript:;">Invite {record.lastName}</a>
                              <Divider type="vertical"/>
                              <a href="javascript:;">Delete</a>
                            </span>
                        )}
                    />
                </Table>
            </div>
        );
    }
}

export default MyTable02;




WrapComponent.js:

import React from 'react';

/**
 * @Description: 高阶函数 - 传参
 * @author weishihuai
 * @date 2019/5/15 16:30
 *
 * 说明: 根据需要对项目中的表格进行定制,如果有非常多的表格,就可以不用每个表格都需要去修改,直接将组件使用高阶组件进行加强即可,比较方便
 *
 * isShowBordered: 表格是否需要边框
 * isShowFooter: 是否展示表格尾部
 * footerContainer: 表格尾部容器
 * Component: 待包装加强的普通组件
 *
 * 属性代理(Props Proxy)
 */
export const wrappedComponent = (isShowBordered, isShowFooter, footerContainer) => (Component) => {
    return class extends React.Component {
        render() {
            const newProps = {
                bordered: isShowBordered,
                footer: isShowFooter ? footerContainer : undefined
            };

            return (
                <Component {...this.props} {...newProps}/>
            )
        }
    }

};



By way of the following transmission parameters:

The result: you can see: MyTable01 and MyTable02 have borders, and custom containers MyTable02 more than a bottom.

MyTable01:

MyTable02:

 

V. Summary

In this paper, two ways React higher-order components: "Property Broker and reverse inheritance" made a brief introduction, and through a number of examples illustrate its use of practical work, the need for specific needs to create higher-order components. it is a good decoupling between business, each higher-order components define your own processing logic. Here is just a brief introduction to the use of HOC, not all use of higher-order components. We can combine the practical application scenarios, try a different usage.

 

Guess you like

Origin blog.csdn.net/Weixiaohuai/article/details/90484564