相信使用过React.js的小伙伴都应该知道其数据双向绑定的灵活性,在处理表单,人机交互方面都具能发挥极大的优势,而且实现起来也比较方便
最近在项目中刚好用到了这系列操作,下面以类似于单选框、复选框和下拉框为例介绍他们在ant-design-pros中的具体实现方式
单选事件
在项目中我们经常会遇到类似单选事件的小功能,比如点击或悬浮的元素高亮,页面上有很多的item元素,要实现点击或者悬浮到哪个就哪个高亮
用jq实现的话,就是选中的元素给加个addClass的
事件,添加active的样式,然后它的兄弟元素removeClass
,去掉active样式
同样的,用react实现,我们可以用相同的思路,比如用一个currentIndex
,通过它来判断是在哪个元素实现切换的,而currentIndex
的值又可以通过选中元素的某个元素属性来动态改变
效果图
代码
class Radio extends PureComponent {
state= {
currentIndex: null,
value: null
}
setCurrentIndex = (e) => {
this.setState({
currentIndex: parseInt(e.currentTarget.getAttribute('index'), 10)
})
}
handleChange = (e) => {
this.setState({
value: e.target.value
})
}
render () {
const arr = ['京东超市', '天猫超市', '京东生鲜', '京东到家', '果蔬好', '盒马生鲜', '无人超市', '每日生鲜']
const itemList = arr.map((item, index) => {
return (
<li
key={index}
className={`${styles.item} ${this.state.currentIndex === index ? styles.active : ''}`}
index={index}
onClick={this.setCurrentIndex}
>
{item}
</li>
)
})
return (
<Card title="单选列表" bordered={false} className={styles.main}>
<div className={styles.content}>
<div className={styles.top}>
<div className={styles.name}>点击或悬浮选中的li高亮:</div>
<ul className={styles.list}>{itemList}</ul>
</div>
<div className={styles.bottom}>
<div className={styles.name}>您的性别为:</div>
<div className={styles.list}>
<label className={styles.item}><input type="radio" name='gender' value="男生" onChange={this.handleChange}/>男生</label>
<label className={styles.item}><input type="radio" name='gender' value="女生" onChange={this.handleChange}/>女生</label>
</div>
<div className={styles.sex}>性别: {this.state.value}</div>
</div>
</div>
</Card>
)
}
}
总的来说,就是生成这些li的时候给元素添加一个index标志位,然后通过相应的点击事件,把这个index用e.currentTarget.getAttribute('index')
取出来,然后去设置currentTarge
t的值,再通过赋给选中的css的active样式就可以了
复选事件
在页面中,有时候会根据用户的一些操作更新属性值,但不同的对象间的操作就涉及到对多个值的状态管理,这些可变的状态通常都保存在组件的状态属性中,并且只能用setState()
方法更新
复选事件和单选事件类似,也需要通过监听各个标签元素的点击或者悬浮等事件来实现
效果图
代码
class Checkbox extends PureComponent {
state = {
fruit: [],
value: []
}
handleChange = (e) => {
let item = e.target.value
let items = this.state.fruit.slice()
let index = items.indexOf(item)
index === -1 ? items.push(item) : items.splice(index, 1)
this.setState({fruit: items})
}
onclickIcon = (e) => {
let item = parseInt(e.currentTarget.getAttribute('index'), 10)
let items = this.state.value.slice()
let index = items.indexOf(item)
index === -1 ? items.push(item) : items.splice(index,1)
this.setState({
value: items
})
}
render() {
const listArr = ['羽绒服', '裙子', '帽子', '围巾']
return (
<Card title="多选列表" bordered={false} className={styles.main}>
<div className={styles.content}>
<div className={styles.left}>
<div className={styles.title}>Choose fruit : </div>
<div className={styles.list}>
<label className={styles.item}><input type="checkbox" name="fruit" value="apple"
onChange={this.handleChange}/>apple</label>
<label className={styles.item}><input type="checkbox" name="fruit" value="banana"
onChange={this.handleChange}/>banana</label>
<label className={styles.item}><input type="checkbox" name="fruit" value="pear"
onChange={this.handleChange}/>pear</label>
</div>
<div>Choosen : {this.state.fruit.join('-')}</div>
</div>
<div className={styles.right}>
<div className={styles.tag}>点亮我的喜欢:</div>
<ul className={styles.card}>
{
listArr.map((item,index) => {
return (
<li
key={index}
className={styles.item}
index={index}
onClick={this.onclickIcon}
>
<svg
className={`${styles.svg} ${this.state.value.indexOf(index) === -1? styles.icon : styles.iconColor}`} aria-hidden="true">
<use xlinkHref={this.state.value.indexOf(index) === -1? "#icon-lovetaoxin" : "#icon-shixintaoxin"}></use>
</svg>
<span to='#' target="_blank" className={styles.text}>{item}</span>
</li>
)
})
}
</ul>
</div>
</div>
</Card>
)
}
}
总的来说,我们通过在组件状态state中定义数组,存放选中的信息,主要是通过先在li元素中添加index标志位,然后通过相应事件的触发,把这个index用e.currentTarget.getAttribute('index')
取出来,存放到state中定义的数组中
需要注意的是,在更新value时,必须使用setState
函数,否则代码不会被重新渲染,在return中显示已选中的选项不会实时更新
下拉事件
下拉的实现方式和单选、复选有些类似,而且在ant-design中也有下拉Dropdown的组件,可以复用加以修改,可以直接看例子说明
效果图
代码
class NewDropdown extends PureComponent {
state = {
value: 'basketball'
}
handleChange = (e) => {
this.setState({
value: e.target.value
})
}
handleMenuClick = ({ key }) => {
if (key === 'radio') {
router.push('/selector/radio');
return;
}
if (key === 'checkbox') {
router.push('/selector/checkbox');
return;
}
if (key === 'dropdown') {
return;
}
};
render () {
const menu = (
<Menu className={styles.menu} selectedKeys={[]} onClick={this.handleMenuClick}>
<Menu.Item key="radio">
<Icon type="user" />
<FormattedMessage id="menu.selector.radio" defaultMessage="radio" />
</Menu.Item>
<Menu.Item key="checkbox">
<Icon type="setting" />
<FormattedMessage id="menu.selector.checkbox" defaultMessage="checkbox" />
</Menu.Item>
<Menu.Item key="dropdown">
<Icon type="close-circle" />
<FormattedMessage id="menu.selector.dropdown" defaultMessage="dropdown" />
</Menu.Item>
</Menu>
);
return (
<Card title="下拉列表" bordered={false} className={styles.main}>
<div className={styles.content}>
<div className={styles.left}>
<label className={styles.select}>choose favorite sports:
<select value={this.state.value} onChange={this.handleChange}>
<option value="running">running</option>
<option value="basketball">basketball</option>
<option value="skiing">skiing</option>
</select>
</label>
<div className={styles.chosen}>chosen: {this.state.value}</div>
</div>
<div className={styles.right}>
<Dropdown overlay={menu}>
<span className={styles.action}>
<span className={styles.name}>查看示例</span>
</span>
</Dropdown>
</div>
</div>
</Card>
)
}
}
总的来说,是通过在组件状态中设置一个value值,并在return中的select标签中使用,上述实现的是一个默认选中的功能
在用react实现表单时,其原理都是相同的,利用一个组件状态state来存储选中信息,然后监听各个标签元素的点击、悬浮、切换等事件,并在响应函数中更新组件状态