如何用react实现一款Radio

1,465 阅读3分钟

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

TIP 👉 莫愁前路无知己,天下谁人不识君。唐·高适《[别董大]

前言

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

Radio 单选框

import


import Radio from '@/components/Radio/Radio';

Props

1. checked
  • 类型:bool

  • 默认值:false

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

2. disabled
  • 类型:bool

  • 默认值:false

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

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

  • 默认值:无

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

  • {boolean} newChecked 新的选中状态

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

  • 默认值:无

  • 说明:最外层元素样式


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

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

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

RadioGroup 单选框组

import


import RadioGroup from '@/components/Radio/RadioGroup';

Props

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

  • 默认值:无

  • 说明:可选项数组

<RadioGroup options = {[{label: "选项一", value: 1}, {label: "选项2", value: 2, disabled: true}]}></RadioGroup>
2. value
  • 类型:any

  • 默认值:无

  • 说明:选中选项value值

3. disabled
  • 类型:bool

  • 默认值:false

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

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

  • 默认值:无

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

  • {any} newGroupValue 新的选中选项value

5. button
  • 类型:bool

  • 默认值:false

  • 说明:是否以按钮样式显示,true表示以按钮样式显示

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

  • 默认值:无

  • 说明:最外层元素样式

<RadioGroup className = {'my-class'}></RadioGroup>
<RadioGroup className = {['my-class1', 'my-class2']}></RadioGroup>
<RadioGroup className = {{'my-class1': true}}></RadioGroup>

实现Radio.js

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

/**
* Radio 单选框
*/
class Radio extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 是否选中, true表示选中
        checked: PropTypes.bool,
        // 是否不可用,true表示单选框不可用,复选框为灰色
        disabled: 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
    }

    constructor(props) {
        super(props);
    }


    /**
    * 选择框最外层需要的class
    * @returns {string}
    */
    getRadioWrapperClassName(){
        return utils.clasx(this.props.className, {
            'radio-wrapper': true,
            'radio-checked': this.props.checked,
            'radio-disabled': this.props.disabled
        });
    }

    /**
    * 点击事件
    */

    checkedToggle(e) {
        if (!this.props.disabled && this.props.onChange) {
            this.props.onChange(!this.props.checked)
        }
    }

    render() {
        return (
            <div
            className={this.getRadioWrapperClassName()}
            onClick={(e) => this.checkedToggle(e)}>
                <span className="radio"></span>
                <span className="radio-text">{this.props.children}</span>
            </div>
        );
    }
}

export default Radio;

实现RadioGroup.js

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

/**
* RadioGroup 单选框组
*/

class RadioGroup extends React.Component {
    // 入参类型检查
    static propTypes = {
        // 可选项数组,例:[{label: '选项一', value: 1}, {label: '选项2', value: 2, disabled: true}]
        options: PropTypes.arrayOf((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.any,
        // 是否不可用,true表示单选框组不可用,该组单选框全部为灰色
        disabled: PropTypes.bool,

        /**
        * 选中选项改变时的回调函数
        * @param {any} newGroupValue 新的选中选项value
        */

        onChange: PropTypes.func.isRequired,
        // 是否显示按钮样式
        button: PropTypes.bool,
        // 最外层元素样式
        className: PropTypes.oneOfType([
            PropTypes.string,
            PropTypes.array,
            PropTypes.objectOf(PropTypes.bool)
        ])
    }

    // 入参默认值
    static defaultProps = {
        disabled: false
    }

    constructor(props) {
        super(props);
        this.state = {
            options: this.getRadioGroupOptionList()
        };
    }

    /**
    * 获取options解析后的本组件要使用的数据
    * 在要props的基础上添加一些属性
    */
    getRadioGroupOptionList(){
        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.value === 'object') {
                    optionItem.checked = JSON.stringify(optionItem.value) === JSON.stringify(this.props.value);
                } else {
                    optionItem.checked = optionItem.value === this.props.value;
                }
                itemOptions.push(optionItem);
            }
        }
        return itemOptions;
    }

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

    /**
    * 点击事件
    * @param {number} idx 当前选项索引值
    * @returns {boolean} newChecked 当前选项的新选中状态
    */

    radioItemChange(idx, newChecked) {
        if (this.props.disabled || !newChecked) {
            return false;
        }

        let newGroupValue = null;
        for (let i = 0; i < this.state.options.length; i++) {
            let optionItem = this.state.options[i];
            if(i === idx){
                optionItem.checked = true;
                newGroupValue = optionItem.value;
            }else {
                optionItem.checked = false;
            }
        }
        this.props.onChange(newGroupValue);
    }

    render() {
        return (
            <div className={this.getRadioGroupClassName()}>
                {this.state.options.map((item, index) =>
                    <Radio
                    key={JSON.stringify(item.value)}
                    checked={item.checked}
                    disabled={item.disabled || this.props.disabled}
                    onChange={(newChecked) => this.radioItemChange(index, newChecked)}>
                    {item.label}
                    </Radio>
                )}
            </div>
        );
    }
}
export default RadioGroup;

「欢迎在评论区讨论」

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