如何封装一款React Checkbox组件

2,105 阅读3分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

TIP 👉 业精于勤荒于嬉,行成于思毁于随。唐·韩愈《进学解》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的轻量,简单,其实这个时候我们并不想采用第三方的框架。

先来看一下实现的效果 image.png

CheckBox 多选框

import


import CheckBox from '@/components/CheckBox/CheckBox';

Props

1. checked
  • 类型:bool

  • 默认值:false

  • 说明:是否选中, true表示选中

2. disabled
  • 类型:bool

  • 默认值:false

  • 说明:是否不可用,true表示复选框不可用,复选框为灰色

3. indeterminate
  • 类型:bool

  • 默认值:false

  • 说明:是否开启不确定状态 (即未全选,checked为false时有效;true表示不确定状态,checked为false时复选框中间显示蓝色小方块)

4. onChange
  • 类型:func (必填)

  • 默认值:无

  • 说明:选中状态改变时的回调函数,入参:

  • {boolean} newChecked 新的选中状态

5. className
  • 类型:string | array | object(key: 样式名,value: bool)

  • 默认值:无

  • 说明:最外层元素样式


<CheckBox className = {'my-class'}></CheckBox>

<CheckBox className = {['my-class1', 'my-class2']}></CheckBox>

<CheckBox className = {{'my-class1': true}}></CheckBox>

CheckBoxGroup 多选框组

import


import CheckBoxGroup from '@/components/CheckBox/CheckBoxGroup';

Props

1. options
  • 类型:array (必填)

  • 默认值:无

  • 说明:可选项数组


<CheckBoxGroup options = {[{label: "选项一", value: 1}, {label: "选项2", value: 2, disabled: true}]}></CheckBoxGroup>

2. value
  • 类型:array

  • 默认值:[]

  • 说明:选中选项value组成的数组

3. disabled
  • 类型:bool

  • 默认值:false

  • 说明:是否不可用,true表示复选框组不可用,该组复选框全部为灰色

4. onChange
  • 类型:func (必填)

  • 默认值:无

  • 说明:组内复选框选中状态改变时的回调函数,入参:

  • {Array} newGroupValue 新的选中选项value组成的数组

5. className
  • 类型:string | array | object(key: 样式名,value: bool)

  • 默认值:无

  • 说明:最外层元素样式


<CheckBoxGroup className = {'my-class'}></CheckBoxGroup>

<CheckBoxGroup className = {['my-class1', 'my-class2']}></CheckBoxGroup>

<CheckBoxGroup className = {{'my-class1': true}}></CheckBoxGroup>

实现CheckBox.js

import React from 'react';
import PropTypes from 'prop-types';
// 为保证测试用例运行,不使用webpack内置utils模块,单独引入
import utils from '../../js/common/utils/index.js';
import './CheckBox.scss';
/**
* CheckBox 复选框
*/

class CheckBox extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 是否选中 (true表示选中)
        checked: PropTypes.bool,
        // 是否不可用 (true表示复选框不可用,复选框为灰色)
        disabled: PropTypes.bool,
        // 是否开启不确定状态 (即未全选,checked为false时有效;true表示不确定状态,checked为false时复选框中间显示蓝色小方块)
        indeterminate: PropTypes.bool,
        /**
        * 选中状态改变时的回调函数
        * @param {boolean} newChecked 新的选中状态
        */
        onChange: PropTypes.func.isRequired,
        // 最外层元素样式
        className: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.array,
            PropTypes.objectOf(PropTypes.bool)
        ])
    }
    // 入参默认值
    static defaultProps = {
        checked: false,
        disabled: false,
        indeterminate: false
    }
    constructor(props) {
        super(props);
        this.state = {
            // 鼠标是否浮动在组件上方
            hover: false
        };
    }

    /**
    * 选择框最外层需要的class
    * @returns {string}
    */

    getCheckboxWrapperClassName(){
        return utils.clasx(this.props.className, {
            'checkbox-wrapper': true,
            'checkbox-checked': this.props.checked,
            'checkbox-disabled': this.props.disabled,
            'checkbox-indeterminate': this.props.indeterminate && !this.props.checked
        });
    }
    /**
    * 点击事件
    * @param e
    * @returns {boolean}
    */

    checkedToggle(e) {
        if (!this.props.disabled && this.props.onChange) {
            this.props.onChange(!this.props.checked);
        }
    }
    render() {
        return (
            <div className={this.getCheckboxWrapperClassName()}
            onClick={(e) => this.checkedToggle(e)}>
                <span className="checkbox"></span>
                <span className="checkbox-text">{this.props.children}</span>
            </div>
        );
    }
}


export default CheckBox;

下面我们来实现多选CheckBoxGroup.js

import React from 'react';
import PropTypes from 'prop-types';
import CheckBox from './CheckBox.js';
// 为保证测试用例运行,不使用webpack内置utils模块,单独引入
import utils from '../../js/common/utils/index.js';
import './CheckBox.scss';
/**
* CheckBoxGroup 复选框组
*/
class CheckBoxGroup extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 可选项数组,例:[{label: "选项一", value: 1}, {label: "选项2", value: 2, disabled: true}]
        options: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
        if (typeof propValue[key] !=='object') {
            return new Error(`Invalid prop \`${propFullName}\` of type \`${typeof propValue[key]}\` supplied to \`${componentName}\`, expected \`object\``);
        }

        if (!propValue[key].hasOwnProperty('label')) {
            return new Error(`\`${componentName}\`组件的 prop 参数\`${propFullName}\`缺少属性\`label\``)
        }

        if (!propValue[key].hasOwnProperty('value')) {
            return new Error(`\`${componentName}\`组件的 prop 参数\`${propFullName}\`缺少属性\`value\``)
        }

        }).isRequired,
        // 选中选项value组成的数组
        value: PropTypes.array,
        // 是否不可用,true表示复选框组不可用,该组复选框全部为灰色
        disabled: PropTypes.bool,

        /**
        * 组内任意选项选中状态改变时的回调函数
        * @param {Array} newGroupValue 新的选中选项value组成的数组
        */

        onChange: PropTypes.func.isRequired,
            // 最外层元素样式
            className: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.array,
            PropTypes.objectOf(PropTypes.bool)
        ])
    }

    // 入参默认值
    static defaultProps = {
        disabled: false,
        value: []
    }
    constructor(props) {
        super(props);
        this.state = {
            options: this.getCheckboxGroupOptionList()
        };
    }

    /**
    * 获取options解析后的本组件要使用的数据
    * 在要props的基础上添加一些属性
    */

    getCheckboxGroupOptionList(){
        let itemOptions = [];
        if (this.props.options && this.props.options instanceof Array) {
            let optionItem;
            for (let i = 0; i < this.props.options.length; i++) {
                optionItem = {...this.props.options[i]};
                if (typeof this.props.options[i].value === 'object' && this.props.value instanceof Array) {
                    optionItem.checked = false;
                    for (let j =0; j < this.props.value.length; j++) {
                        if (JSON.stringify(optionItem.value) === JSON.stringify(this.props.value[j])) {
                            optionItem.checked = true;
                            break;
                        }
                    }
                } else {
                    optionItem.checked = this.props.value.includes(optionItem.value);
                }
                itemOptions.push(optionItem);
            }
        }
        return itemOptions;
    }

    /**
    * 选择框组最外层需要的class
    * @returns {string}
    */
    getCheckboxGroupClassName(){
        return utils.clasx(this.props.className, {
            'checkbox-group': true,
            'checkbox-group-disabled': this.props.disabled
        })
    }

    /**
    * 点击事件
    * @param {number} idx 当前选项索引值
    * @returns {boolean} newChecked 当前选项的新选中状态
    */
    itemToggle(idx, newChecked) {
        this.state.options[idx].checked = newChecked;
        let newGroupValue = [];
        for (let i = 0; i < this.state.options.length; i++) {
            let optionItem = this.state.options[i];
            if(optionItem.checked){
                newGroupValue.push(optionItem.value);
            }
        }
        this.props.onChange(newGroupValue)
    }
    render() {
        return (
            <div className={this.getCheckboxGroupClassName()}>
                {this.state.options.map((item, index) =>
                    <CheckBox
                        key={JSON.stringify(item.value)}
                        checked={item.checked}
                        disabled={item.disabled || this.props.disabled}
                        onChange={(newChecked) => this.itemToggle(index, newChecked)}>
                        {item.label}
                    </CheckBox>
                )}
            </div>
        );
    }
}

export default CheckBoxGroup;

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下