动手打造antd组件之checkbox

538 阅读3分钟

本文正在参加「金石计划」

一:引言

本篇文章仿写antd组件库的checkbox组件,文章重点是模拟checkbox组件的内部功能实现,样式比较丑希望大家包涵。

二:组件分析

观察下图,我们设计一个可配置的多选组件,其配置项包含如下

单选

image.png

多选

image.png

  • className 类名
  • value 当前指定选中的
  • disabled 禁止选中
  • onChange 选项发生变化时回调

三:问题拆解

在设计组件前,我们可以先考子问题,最后组装起来就实现了目标组件。

问题1:如何控制多个选择框的状态(选中,不选中,禁止)?

  • 初始在CheckGroup父组件初始化选中数组,给每个子组件Check的props上添加onChange函数和checked状态,当子组件的选中状态更新时,通过onChange函数可以间接的修改选中数组。从而做到数据驱动多个选择框的试图修改。

问题2:如何在CheckGroup的组件上拿到所有选中的状态?

  • CheckGroup父组件初始化时会创建一个选中数组,当任何子组件状态改变时该数组都会被修改,用户可以在每次子组件状态改变时拿到最新的所有选中状态。

五:Checkbox代码实现

CheckGroup代码

  1. CheckGroup根据用户传递的value(可选)初始化当前选中的状态数组。

  2. 通过 React.Children.map结合React.cloneElement方法动态为子组件Check添加checked状态和disbled状态,并绑定一个onChange回调函数。

    import React,{ useState } from "react";
     import classNames from "classnames";
     interface CheckGroupProps {
       className?:string,
       children?:React.ReactNode,
       value?:any[]//默认选中的数据
       onChange?:(e:any)=>void
     }
     const Check = (props:CheckGroupProps) => {
       const {className,children,value,onChange} = props;
       const [activeChoices,setActiveChoices] = useState(value?value:[]);//当前所有选中的
       const classes = classNames('check',className);
    
    
       //子组件Check状态修改时触发
       const checkClick = (val:any,isChoice:boolean) => {
         //如果是ture,移出,否则添加
         let tmp = [...activeChoices];
         if(isChoice) {
           if(tmp.includes(val)) {
             let index = tmp.indexOf(val);
             tmp.splice(index,1);
           }
         }else{
           tmp.push(val);
         }
         onChange && onChange(tmp);
         setActiveChoices(tmp);
       }
       //动态为子组件添加checked状态和disabled状态
       const checkChild = () => {
         let res = React.Children.map(children,(item:any)=>{
           if(item?.type?.name!=='Check') {
             console.error('子元素必须是Check组件');
             return null;
           }
           else{
             const val = item.props.value;//
             //如果本身是checked,添加数组
             const isChoice = activeChoices.includes(val);
             return React.cloneElement(item,{
               disabled:item.props.disabled,
               checked:isChoice,
               onChange:checkClick
             })
           }
         })
         return res;
       }
       return (
         <div className={classes}>
           {checkChild()}
         </div>
       )
     }
     export default Check;
    

Check代码

Check的逻辑处理比较简单,只需要根据props动态的获取自己的选择状态和禁止状态,唯一需要关心的是当自己选择状态更新时,需要调用父组件CheckGroup传递的onChange动态的修改父组件的选中数组。

    import React from "react";
    import classNames from "classnames";
    interface CheckProps {
      className?:string,
      children?:React.ReactNode,
      value?:any,
      disabled?:boolean,
      checked?:boolean,
      onChange?:(e:any,isChoice:boolean)=>void
    }
    const Check = (props:CheckProps) => {
      const {className,children,value,checked,disabled,onChange} = props;
      const classes = classNames('check',className);
      return (
        <span className={classes}>
          <input 
            type='radio' 
            disabled={disabled} 
            checked={checked} 
            onClick={(e)=>{
              onChange && onChange(value,checked!);
            }}
          />
          <text>{children}</text>
        </span>
      )
    }
    export default Check;

六:功能演示

演示1:默认

9.gif

<CheckGroup>
    <Check value={'苹果'}>苹果</Check>
    <Check value={'香蕉'}>香蕉</Check>
    <Check value={'菠萝'}>菠萝</Check>
  </CheckGroup>

演示2:添加初始化选中

10.gif

<CheckGroup value={['香蕉','菠萝']}>
    <Check value={'苹果'}>苹果</Check>
    <Check value={'香蕉'}>香蕉</Check>
    <Check value={'菠萝'}>菠萝</Check>
  </CheckGroup>

演示3:添加禁用状态

image.png

<CheckGroup value={['菠萝']}>
    <Check value={'苹果'}>苹果</Check>
    <Check value={'香蕉'} disabled>香蕉</Check>
    <Check value={'菠萝'}>菠萝</Check>
  </CheckGroup>

演示3:回调函数监听

11.gif

<CheckGroup onChange={(e)=>{console.log(e)}}>
    <Check value={'苹果'}>苹果</Check>
    <Check value={'香蕉'}>香蕉</Check>
    <Check value={'菠萝'}>菠萝</Check>
  </CheckGroup>

总结

今天CheckBox组件到此结束,希望大家多多支持,我们下一个组件见。