如何完整的编写一个React通用组件?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Donspeng/article/details/83421136

React组件概念想必大家都熟悉了,但是在业务开发过程中,面对通用的业务,怎么编写一个通用的React业务组件呢?本文以实际案例说明,如何编写一个通用的业务组件。

案例背景

实现一个如图所示的组件,可以添加对组件进行添加和删除

在这里插入图片描述

实现思路

  • 实现组件的界面样式
  • 实现组件的基本功能
  • 考虑通用型和可扩展性

实现基本的界面样式

实现上面的界面展示,需要三个方面的东西

  • 一个文本框
  • 一个➖操作
  • 一个➕操作
    jrs可以自己编写样式,也可以引用通用的组件,本文偷个懒,决定引用现成的antd的组件
import React, { Component } from 'react';
import {Input, Icon} from 'antd';
import PropTypes from 'prop-types';

class List extends Component{
    render() {
        let iconStyle = {
            del: {
               color: 'red', marginLeft: '0.2%'
            },
            add: {
               color: 'grey', marginLeft: '0.2%'
            }
        }
        return <div style = {{display: 'flex', marginLeft:'10px'}}>
                   <Input/>
                   <Icon type="minus-circle" theme="outlined" style = {iconStyle.del}/>
                   <Icon type="plus-circle" theme="outlined" style = {iconStyle.add}/>
               </div>
    }
}

上面就把基本的样式搞定了,这离我们想要的还缺一些基本的功能

实现基本功能

组件处于编辑状态的时候

  • 点击➕,增加组件
  • 点击➖,减少相应组件
  • 当只有一个组件的时候,只显示➕
    组件处于非编辑状态的时候
  • 输入框不能编辑
  • ➕和➖都不展示
    实现的思路就是,组件内部维护一个list,然后每次增加和删除的时候,对list进行增加和删除,然后渲染整个组件。
class List extends Component{
    constructor(props) {
        super(props);
        this.state = {
            list : [1]
        }
    }
    add = (key) => () => {
        let list = this.state.list;
        list.push(1);
        this.setState({
            list: list
        });
    }
    del = (key) => () => {
        let list = this.state.list;
        list.splice(key, 1, 0);
        this.setState({
            list: list
        });
    }
    renderList = () => {
        let list = this.state.list;
        let renderList = [];
        let len = list.length;
        let delShow = list.reduce((total, value) => total + value);
        let iconStyle = {
            del: {
                color: 'red', marginLeft: '0.2%'
            },
            add: {
                color: 'grey', marginLeft: '0.2%'
            }
        }
        for (let i =0; i < len; i++) {
            list[i] && renderList.push(<div key = {i} style = {{display: 'flex', marginLeft:'10px'}}>
                <Input/>
                {delShow > 1 && <Icon type="minus-circle" theme="outlined" style = {iconStyle.del} onClick = {this.del(i)}/>}
                <Icon type="plus-circle" theme="outlined" style = {iconStyle.add} onClick = {this.add(i)}/>
            </div>)
        }
        return renderList;
    }
    render() {
        return this.renderList();
    }
}

实现了上述组件,可以进行添加和删除了,但是这个组件具备一个完整的组件还需要一些功能

  • 可以获取组件的值
  • 可以设置组件的默认值
  • 组件可以设置非编辑状态
  • 可以设置组件的样式
class List extends Component{
    constructor(props) {
        super(props);
        this.state = {
            list : [1]
        }
    }
    static defaultProps = {
        iconStyle: {
            del: {
                color: 'red', marginLeft: '0.2%'
            },
            add: {
                color: 'grey', marginLeft: '0.2%'
            }
        },
        // 提供默认的样式选择
        layoutStyle: {
            display: 'flex',
            width: '40%',
            flexDirection: 'row',
            flexWrap: 'wrap',
            justifyContent: 'flex-start'
        },
        // 通过disabled控制组件的状态
        disabled: false
    }
    add = (key) => () => {
        let list = this.state.list;
        list.push(1);
        this.setState({
            list: list
        });
    }
    del = (key) => () => {
        let list = this.state.list;
        list.splice(key, 1, 0);
        this.setState({
            list: list
        });
        // console.log('exce del action', key);        
    }
    // 获取组件的值,本文中默认所有组件都实现getValue方法,然后通过ref方法获得
    getValue = () => {
        let list = this.state.list;
        let len = list.length;
        let value = [];
        for(let i = 0; i< len; i++) {
            if(list[i]) {
                value.push(this.refs[i].getValue())
            }
        }
        return value;
    }
    
    renderList = (value) => {
        let list = this.state.list;
        let renderList = [];
        let len = list.length;
        let delShow = list.reduce((total, value) => total + value);
        let { iconStyle, disabled} = this.props;
        for (let i =0; i < len; i++) {
            list[i] && renderList.push(<div key = {i} style = {{display: 'flex', marginLeft:'10px'}}>
                <Input ref= {i} defaultValue = {value && value[i]} disabled = {disabled}/>
                {!disabled && delShow > 1 && <Icon type="minus-circle" theme="outlined" style = {iconStyle.del} onClick = {this.del(i)}/>}
                {!disabled && <Icon type="plus-circle" theme="outlined" style = {iconStyle.add} onClick = {this.add(i)}/>}
            </div>)
        }
        return renderList;
    }
    render() {
        let { value, layoutStyle } = this.props;
        
        return <div style = {layoutStyle}>
            // 通过value设置组件的默认值
            {this.renderList(value)}
        </div>
    }
}

经过上面的编写,终于完成了组件的基本样式和功能,基本上是够用的了,但是组件还存在一些问题,通用性不够。

实现更通用的组件

  • 如果不仅仅展示一个input,可能是任意其他任何一个组件怎么办
  • 如果想在组件中间添加,一些特别的字符或者组件,比如且或非逻辑怎么办?
    先来思考第一个
    如果组件不是input而是其他的,怎么办?怎么获取它的值怎么设置它的状态?
    如果换成其他的组件,当然很简单,在多一个参数,把这个参数作为组件就可以。
    ok,这样是可行的,但是怎么获取它的值呢?以及怎么设置他的状态?
    简单来说,就是怎么把 ref = i 注入到组件中去?
    答:实现一个高阶组件就可以了啊。
class List extends Component{
    constructor(props) {
        super(props);
        this.state = {
            list : [1]
        }
    }
    static defaultProps = {
        iconStyle: {
            del: {
                color: 'red', marginLeft: '0.2%'
            },
            add: {
                color: 'grey', marginLeft: '0.2%'
            }
        },
        layoutStyle: {
            display: 'flex',
            width: '40%',
            flexDirection: 'row',
            flexWrap: 'wrap',
            justifyContent: 'flex-start'
        },
        disabled: false
    }
    add = (key) => () => {
        let list = this.state.list;
        list.push(1);
        this.setState({
            list: list
        });
    }
    del = (key) => () => {
        let list = this.state.list;
        list.splice(key, 1, 0);
        this.setState({
            list: list
        });
        // console.log('exce del action', key);        
    }
    getValue = () => {
        let list = this.state.list;
        let len = list.length;
        let value = [];
        for(let i = 0; i< len; i++) {
            if(list[i]) {
                value.push(this.refs[i].getValue())
            }
        }
        return value;
    }
    renderList = (value) => {
        let list = this.state.list;
        let renderList = [];
        let len = list.length;
        let delShow = list.reduce((total, value) => total + value);
        // InputComponent 就是你的输入组件 ,after则是你需要插入的后缀组件
        let { iconStyle, disabled, InputComponent, after} = this.props;
        for (let i =0; i < len; i++) {
            list[i] && renderList.push(<div key = {i} style = {{display: 'flex', marginLeft:'10px'}}>
                <InputComponent ref = {i} defaultValue = {value && value[i]} disabled = {disabled}/>
                {delShow > 1 && after}
                {!disabled && delShow > 1 && <Icon type="minus-circle" theme="outlined" style = {iconStyle.del} onClick = {this.del(i)}/>}
                {!disabled && <Icon type="plus-circle" theme="outlined" style = {iconStyle.add} onClick = {this.add(i)}/>}
            </div>)
        }
        return renderList;
    }
    render() {
        let { value, layoutStyle } = this.props;
        
        return <div style = {layoutStyle}>
            {this.renderList(value)}
        </div>
    }
}

至此,整个功能就编写完成了。
那么还缺一点东西,那就是参数说明

List.propTypes = {
    value: PropTypes.array,
    layoutStyle: PropTypes.object,
    iconStyle: PropTypes.object,
    InputComponent: PropTypes.element.isRequired,
    after: PropTypes.node,
    disabled: PropTypes.bool
};

附录

附上完整代码

import React, { Component } from 'react';
import {Input, Icon} from 'antd';
import PropTypes from 'prop-types';

class List extends Component{
    constructor(props) {
        super(props);
        this.state = {
            list : [1]
        }
    }
    static defaultProps = {
        iconStyle: {
            del: {
                color: 'red', marginLeft: '0.2%'
            },
            add: {
                color: 'grey', marginLeft: '0.2%'
            }
        },
        layoutStyle: {
            display: 'flex',
            width: '40%',
            flexDirection: 'row',
            flexWrap: 'wrap',
            justifyContent: 'flex-start'
        },
        disabled: false
    }
    add = (key) => () => {
        let list = this.state.list;
        list.push(1);
        this.setState({
            list: list
        });
    }
    del = (key) => () => {
        let list = this.state.list;
        list.splice(key, 1, 0);
        this.setState({
            list: list
        });
        // console.log('exce del action', key);        
    }
    getValue = () => {
        let list = this.state.list;
        let len = list.length;
        let value = [];
        for(let i = 0; i< len; i++) {
            if(list[i]) {
                value.push(this.refs[i].getValue())
            }
        }
        return value;
    }
    renderList = (value) => {
        let list = this.state.list;
        let renderList = [];
        let len = list.length;
        let delShow = list.reduce((total, value) => total + value);
        let { iconStyle, disabled, InputComponent, after} = this.props;
        for (let i =0; i < len; i++) {
            list[i] && renderList.push(<div key = {i} style = {{display: 'flex', marginLeft:'10px'}}>
                {/* <Input ref= {i} defaultValue = {value && value[i]} disabled = {disabled}/> */}
                <InputComponent ref = {i} defaultValue = {value && value[i]} disabled = {disabled}/>
                {delShow > 1 && after}
                {!disabled && delShow > 1 && <Icon type="minus-circle" theme="outlined" style = {iconStyle.del} onClick = {this.del(i)}/>}
                {!disabled && <Icon type="plus-circle" theme="outlined" style = {iconStyle.add} onClick = {this.add(i)}/>}
            </div>)
        }
        return renderList;
    }
    render() {
        let { value, layoutStyle } = this.props;
        
        return <div style = {layoutStyle}>
            {this.renderList(value)}
        </div>
    }
}
List.propTypes = {
    value: PropTypes.array,
    layoutStyle: PropTypes.object,
    iconStyle: PropTypes.object,
    InputComponent: PropTypes.element.isRequired,
    after: PropTypes.node,
    disabled: PropTypes.bool
};
export default List;
展开阅读全文

没有更多推荐了,返回首页