本文正在参加「金石计划」
一:引言
本篇文章仿写antd组件库的checkbox组件,文章重点是模拟checkbox组件的内部功能实现,样式比较丑希望大家包涵。
二:组件分析
观察下图,我们设计一个可配置的多选组件,其配置项包含如下
单选
多选
- className 类名
- value 当前指定选中的
- disabled 禁止选中
- onChange 选项发生变化时回调
三:问题拆解
在设计组件前,我们可以先考子问题,最后组装起来就实现了目标组件。
问题1:如何控制多个选择框的状态(选中,不选中,禁止)?
- 初始在CheckGroup父组件初始化选中数组,给每个子组件Check的props上添加onChange函数和checked状态,当子组件的选中状态更新时,通过onChange函数可以间接的修改选中数组。从而做到数据驱动多个选择框的试图修改。
问题2:如何在CheckGroup的组件上拿到所有选中的状态?
- CheckGroup父组件初始化时会创建一个选中数组,当任何子组件状态改变时该数组都会被修改,用户可以在每次子组件状态改变时拿到最新的所有选中状态。
五:Checkbox代码实现
CheckGroup代码
-
CheckGroup根据用户传递的value(可选)初始化当前选中的状态数组。
-
通过 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:默认
<CheckGroup>
<Check value={'苹果'}>苹果</Check>
<Check value={'香蕉'}>香蕉</Check>
<Check value={'菠萝'}>菠萝</Check>
</CheckGroup>
演示2:添加初始化选中
<CheckGroup value={['香蕉','菠萝']}>
<Check value={'苹果'}>苹果</Check>
<Check value={'香蕉'}>香蕉</Check>
<Check value={'菠萝'}>菠萝</Check>
</CheckGroup>
演示3:添加禁用状态
<CheckGroup value={['菠萝']}>
<Check value={'苹果'}>苹果</Check>
<Check value={'香蕉'} disabled>香蕉</Check>
<Check value={'菠萝'}>菠萝</Check>
</CheckGroup>
演示3:回调函数监听
<CheckGroup onChange={(e)=>{console.log(e)}}>
<Check value={'苹果'}>苹果</Check>
<Check value={'香蕉'}>香蕉</Check>
<Check value={'菠萝'}>菠萝</Check>
</CheckGroup>
总结
今天CheckBox组件到此结束,希望大家多多支持,我们下一个组件见。