学习就比如是座大山,人们沿着不一样的路爬山,分享着本身看到的风景。你不必定能看到别人看到的风景,体会到别人的心情。只有本身去爬山,才能看到不同的风景,体会更加深入。一千个读者就有一千个哈姆雷特,可是莎士比亚心中的哈姆雷特确定只有一个。就比如element源码只有一个,但每一个人看到的都是不同的风景。element源码解读是一个系列,每个组件细腻的背后都能看到前端工程师付出的心血,本篇带来的是Element源码分析系列4-Radio(单选框)css
单选框这个组件看似简单,实则知识点众多,较为复杂,若是写一个html的原生单选框,那确实很简单,可是封装一个完整的单选组件就不那么简单了。element团队在整个radio组件的设计和构思真可谓十分的细致,没有一行多余的代码。从基础的css到选择逻辑都无不彰显其巧夺天工的思想。html
咱们都知道原生的radio标签很丑,样式在各个浏览器不统一,做为一个组件不可能就这样屈服,何况人这种物种老是但愿统驾驭全部的物质之上,包括代码。因此必须本身实现全部radio按钮的样式,那么如何本身实现一个可控制的radio标签呢?看element是怎么实现的。 前端
<label className='el-radio'>
<span>
<span className="el-radio__inner"></span>
<input
type="radio"
className="el-radio__original"
/>
</span>
<span className="el-radio__label">
{children || value}
</span>
</label>
复制代码
问题一:如何作到隐藏原始radio标签默认样式?做者巧用opacity:0,真正的input透明度为0,且是绝对定位脱离文档流,所以不占空间且咱们看不到,注意不是display:none或者visibility:hidden,若是是none或者hidden的话则没法触发鼠标点击了,因此只有opacity:0才能达到目的.react
.el-radio__original {
opacity: 0;
outline: 0;
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
}
复制代码
如何作到点击旋钮显示不一样的状态。经过radio标签可分析出,点击便可以理解为一个事件(这里用onChange表示)。经过点击事件传递一个props值(checked)显示不一样的状态。因此在input上必定会有一个事件和一个所要传递的状态。git
<input
type="radio"
className="el-radio__original"
checked={checked} // 传递的状态
onChange={this.onChange.bind(this)} // 事件
/>
复制代码
那么接下来就是定义事件了,其核心代码为:github
constructor(props) {
super(props);
this.state = {
checked: this.getChecked(props) // 定义一个state,受props影响
};
}
componentWillReceiveProps(props) {
const checked = this.getChecked(props);
if (this.state.checked !== checked) { // 维持每次点击时只选中一个
this.setState({ checked });
}
}
getChecked(props) {
return Boolean(props.checked) // 根据传递的props,输出true/false
}
onChange(e) {
const checked = e.target.checked; // 定义被点击项的checked为true
if (checked) {
if (this.props.onChange) {
this.props.onChange(this.props.value); // 向外暴露一个onChange事件并携带value值
}
}
this.setState({ checked }); // 更新被点击项的state为true
}
复制代码
若是你对上述描述的仍是雨里雾里,那么下面这张图能够更好的帮组你理解element-radio标签事件的逻辑: 数组
单选框组故名思议是将全部的radio包裹一层,由最外层原始事件来决定每一个radio的状态。故须要用到React的两个API:React.Children.map、React.cloneElement浏览器
技术扩展:bash
React.Children.map(children, function[(thisArg)])前端工程师
在 children 里的每一个直接子节点上调用一个函数,并将 this 设置为 thisArg。若是 children 是一个数组,它将被遍历并为数组中的每一个子节点调用该函数。若是子节点为 null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。
React.cloneElement(element, [props], [...children])
先了解这两个API,在来看看element中Radio.Group的源码就很简单来。
<div ref='RadioGroup' className='el-radio-group'>
{
React.Children.map(this.props.children, element => {
if (!element) {
return null
}
return React.cloneElement(element, Object.assign({}, element.props, {
onChange: this.onChange.bind(this),
value: this.props.value,
}))
})
}
</div>
复制代码
经过遍历全部的radio组件,克隆出相应的radio组件,并为其附上radio的一些属性,包括方法和value。
纵观整个radio的设计和实现,每一个设计的过程都十分的微妙,尤为是radio如何更新状态,在平时业务中,咱们也常常会遇到这种需求,可是真正的实现起来却没有这么简洁。源码的学习或多或少可让咱们加强本身的代码能力和业务能力。那么跟随个人脚步,带你一步一步解析整个element源码的奇思妙想。山再也不高,有仙则灵;水不在深,有心则成。后续将推出更多源码解析文章。源码请👇这里element-radio源码