react实现表单事件

相信使用过React.js的小伙伴都应该知道其数据双向绑定的灵活性,在处理表单,人机交互方面都具能发挥极大的优势,而且实现起来也比较方便

最近在项目中刚好用到了这系列操作,下面以类似于单选框、复选框和下拉框为例介绍他们在ant-design-pros中的具体实现方式

单选事件

在项目中我们经常会遇到类似单选事件的小功能,比如点击或悬浮的元素高亮,页面上有很多的item元素,要实现点击或者悬浮到哪个就哪个高亮

用jq实现的话,就是选中的元素给加个addClass的事件,添加active的样式,然后它的兄弟元素removeClass,去掉active样式

同样的,用react实现,我们可以用相同的思路,比如用一个currentIndex,通过它来判断是在哪个元素实现切换的,而currentIndex的值又可以通过选中元素的某个元素属性来动态改变

效果图

image

代码

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')取出来,然后去设置currentTarget的值,再通过赋给选中的css的active样式就可以了

复选事件

在页面中,有时候会根据用户的一些操作更新属性值,但不同的对象间的操作就涉及到对多个值的状态管理,这些可变的状态通常都保存在组件的状态属性中,并且只能用setState()方法更新

复选事件和单选事件类似,也需要通过监听各个标签元素的点击或者悬浮等事件来实现

效果图

image

代码

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的组件,可以复用加以修改,可以直接看例子说明

效果图

image

代码

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来存储选中信息,然后监听各个标签元素的点击、悬浮、切换等事件,并在响应函数中更新组件状态