写在前面的话
h5为我们提供了select标签, 如果只是为了基本需求, 使用原生的就好, 然而作为一个有追求的咸鱼, 如何能够忍受原生那丑陋的UI呢, 因此我决定自己定制一个下拉框.
注: 使用原生select, 可以使用appearance:none;清除默认的箭头样式
复制代码
开始定制
本文的写作环境为react 16.3.2 , css方面使用了stylus预编译器, 请一一对照, 废话不多说, 直接上代码.
// Select.js
//本示例使用了CSS3 @keyframes和animation , ie要到10才支持, 请注意兼容性
import React, { Component } from 'react'
import arrowImg from '../images/arrow.png' //本地资源的一张图片
export default class Select extends Component {
constructor(props){
super(props)
this.state = {
select: '', //选中的值
isOption: false, //是否展示下拉菜单
options: this.props.valueArr|| [] //下拉菜单中的数组
}
// 绑定this
this.selectShow = this.selectShow.bind(this)
this.selectHiden = this.selectHiden.bind(this)
this.selectValue = this.selectValue.bind(this)
}
//显示下拉菜单
selectShow(){
this.setState({ isOption: true })
}
//隐藏下拉菜单
selectHiden(){
this.setState({ isOption: false })
}
//选择option
selectValue(e){
let { id } = e.currentTarget.dataset
let { options } = this.state
this.setState({ select: options[id], isOption: false }, this.props.cb&&this.props.cb(options[id]))
// cb为父级传入的函数, 简单示例
// <Select cb={ value=> console.log(value) } />
}
render(){
let { select, isOption, options } = this.state
select = select? select : options[0]
return (
<div className='select-container' >
<div className={!isOption?'select-value': 'select-value-select'} onClick={ this.selectShow } >
{select}<img alt='箭头' src={arrowImg} className={isOption?'select-arrow' : 'select-arrow-show'} />
</div>
<div className={isOption ? 'select-moadl':''} onClick={ this.selectHiden } ></div>
<div className={isOption?'select-option-position': 'select-none'} >
<div className={isOption? 'select-option-container' : ''} >
{
options.map((item, index) =>
<div className='select-option' key={ index } data-id={ index } onClick={ this.selectValue } >{item}</div>)
}
</div>
</div>
</div>
)
}
}
复制代码
//select.styl
.select-container
width 200px
display inline-block
text-align center
.select-value
border-radius 5px
border 1px solid rgba(0,0,0,0.3)
position relative
line-height 1
padding 5px 0
.select-value:hover
border 1px solid #40a9ff
.select-value-select
border-radius 5px
border 1px solid #40a9ff
position relative
line-height 1
padding 5px 0
.select-arrow
position absolute
right 10px
width 20px
height 20px
animation rotateArrow 1s
animation-fill-mode forwards
.select-arrow-show
position absolute
right 10px
width 20px
height 20px
animation rotateArrowShow 1s
animation-fill-mode forwards
@keyframes rotateArrow {
from { transform:rotate(0deg); }
to { transform: rotate(180deg) }
}
@keyframes rotateArrowShow{
from { transform: rotate(180deg) }
to { transform: rotate(0deg) }
}
.select-moadl
position absolute
top 0
left 0
right 0
bottom 0
z-index 10
// background red
.select-option-position
position relative
.select-option-container
position absolute
z-index 11
background rgba(0,0,0,0.15)
width 200px
.select-option:hover
background #40a9ff
color white
.select-none
display none
复制代码
项目使用
import React from 'react'
import Select from './Select'
let valueArr = [2017,2018,2019]
const App = () => (
<div>
<Select cb= { value => console.log(value) } valueArr={ valueArr } />
</div>
)
复制代码
这样, 自作版的下拉框就实现了, 样式如下图
关于下拉框的补充说明
因为本组件使用了自有生命周期来管理内部状态, 因此当你在项目中传入的valueArr在渲染之前就已经有值, 那么一切好说, 但是在正常使用过程中, valueArr 很可能还没有值, 就已经开始渲染了, 导致之后有值, 但是传进去也无效了, 因此需要告诉它, 记得更新数据, 加入react的生命周期
//react 16.3
statice getDerivedStateFromProps(nextProps, preProps){
if(nextProps.valueArr.length){
return { options: nextProps.valueArr }
}
else return nextProps
}
//react 16.3 之前的
componentWillReceiveProps(nextProps){
if(nextProps.valueArr.length){
this.setState({options: nextProps.valueArr } )
}
}
复制代码
这样就能解决异步传输问题了