react写一个select组件的实现代码


Posted in Javascript onApril 03, 2019

之前一直用的antd的Select组件,但在有些端并不适用,而原生的select样式修改不灵活,遂产生自己写一个组件的想法。观察select组件:

<select onChange={(value) => {this.value=value}}
  <option value='1'>man</option>
  <option value='0'>woman</option>
</select>

可以看出数据都是在option中,有值value和显示出来的数据一一对应。如果我们写一个select组件,那么应该有onChange方法,应该要访问到子元素,而且div是没有value这个属性的,所以option应该也是一个组件,有value属性。下面是我写的组件的用法:

import {MobileSelect, MobileOption} from '../../components/MobileSelect';

 <MobileSelect
  disabled={isDisabled}
  value={data.clarity || ringResponse.clarity || 'Flawless'}
  style={{ width: '132px' }}
  onChange={(v) => this.changeDataValue('clarity', v)}
 >
  {
   (clarity || []).map((item, i) => {
    return (
     <MobileOption key={i + ''} value={item.code}>{item.title}</MobileOption>
    );
   })
  }
 </MobileSelect>

可以看出其和一般的select组件用法差不多。效果如下:

react写一个select组件的实现代码

下面是组件

import {observable} from 'mobx';
import {observer} from 'mobx-react';
import React from 'react';
import {Icon} from 'antd';
import './index.less';

interface IProps {
 disabled?: boolean;
 onChange?: (value) => void;
 value?: string | number;
 style?: React.CSSProperties;
 className?: string;
}
@observer
export class MobileSelect extends React.Component<IProps> {
 @observable showOption = false;   // 是否弹出下拉框
 @observable value: any = '';    // 当前选中的value值
 @observable text: any = '';     // 选中的value值对应的文本
 @observable cell: any;       // 组件的dom节点
 componentDidMount(): void {
  // 获取选择框的ref,当在组件外点击时的时候收起下拉框
  document.addEventListener('click', (e) => {
   if (this.cell && this.cell !== e.target && !this.cell.contains(e.target)) {
    this.showOption = false;
   }
  }, true);
 }
 componentWillReceiveProps(nextProps: Readonly<IProps>, nextContext: any): void {
  // 根据传入的value值,遍历children,找到对应值的展示文本
  if (nextProps.value !== this.props.value || nextProps.children !== this.props.children) {
   React.Children.map(this.props.children, (child, index) => {
    if (nextProps.value === child.props.value) {
     this.text = child.props.children;
    }
   });
  }
 }
 render(): React.ReactNode {
  const {children, value} = this.props;
  console.log(value);
  return (
   <div
    className={'Mobile-Select ' + this.props.className}
    style={this.props.style}
    ref={(node) => this.cell = node}
   >
    <div
     className={'select-wrap'}
     onClick={() => {
      // 禁用不能弹出下拉框
      if (!this.props.disabled) {
       this.showOption = !this.showOption;
      }
     }}
    >
     <Icon type='down' style={this.showOption ? {transform: 'rotate(180deg)'} : {transform: 'rotate(0deg)'}} className={'select-icon'}/>
     {this.text}
    </div>
    <div className={'option-wrap'} style={this.showOption ? {position: 'absolute'} : {display: 'none'}}>
     {
      React.Children.map(children, (child, index) => {
       // 设置选中option和未选中option的样式
       let optionClassName = '';
       if (this.props.value === child.props.value) {
        optionClassName = child.props.className ? child.props.className + ' option-item selected' : 'option-item selected';
       } else {
        optionClassName = child.props.className + ' option-item';
       }
       return (
        <div
         onClick={() => {     // 为了在父组件给子组件添加onClick事件,包裹了一层div
          // 有无onChange事件都能改变值
          if (this.props.value && this.props.onChange) {
           this.props.onChange(child.props.value);
          } else {
           this.text = child.props.children;
           this.value = child.props.value;
          }
          console.log(this.value);
          this.showOption = !this.showOption;
         }}
         style={this.props.style}
         className={optionClassName}
        >{child}</div>
       );
      })
     }
    </div>
   </div>
  );
 }
}
interface OptionProps {
 value?: string | number;
 className?: string;
 style?: React.CSSProperties;
}
export class MobileOption extends React.Component<OptionProps> {
 render(): React.ReactNode {
  const {children} = this.props;
  return (
   <div style={this.props.style}>
    {children}
   </div>
  );
 }
}

下面是组件的样式

.Mobile-Select {
 display: inline-block;
 min-width: 100px;
 margin: 0 6px;
 .select-wrap {
  border: 1px solid #e0c0a2;
  border-radius: 4px;
  padding: 5px 11px;
  display: flex;
  flex-direction: row-reverse;
  justify-content: space-between;
  align-items: center;
  .select-icon {
   transition: .3s;
   float: right;
  }
 }
 .option-wrap {
  box-shadow: 0 0 5px #333;
  z-index: 1000;
  border-radius: 5px;
  .option-item {
   background-color: #fff;
   padding: 2px 11px;
   min-width: 100px;
   &.selected {
    background-color: #fbe6d0;
   }
  }
 }
}

总的来说只实现了select的基本功能。有改进的地方请指点一二。

PS:React Select默认值选中问题

import React from "react";
import { render } from "react-dom";

class App extends React.Component {
 constructor(props) {
  super(props);
  this.state = {
   projects: [],
   value: ""
  };
 }
 componentDidMount() {
  // 模拟ajax调用,成功之后把需要改变的默认值赋值给this.state.value
  setTimeout(() => {
   this.setState({
    projects: [
     { id: 1, name: "花生" },
     { id: 2, name: "苹果" },
     { id: 3, name: "杨桃" }
    ],
    value: 1
   });
  }, 3000);
 }
 handleClick() {
  this.setState({
   projects: [
    { id: 4, name: "水果" },
    { id: 5, name: "西瓜" },
    { id: 6, name: "哈哈哈" }
   ],
   value: 4
  });
 }
 handleChange = e => {
  this.setState({
   value: e.target.value
  });
 };
 render() {
  let projects = this.state.projects;
  return (
   <div>
    <button onClick={this.handleClick.bind(this)}>异步拉取数据</button>
    {/* 这里不用再去判断project的长度是否大于0,在ajax里面做判断就行,如果小于零或者不存在它就是默认值 */}
    <select
     defaultValue=""
     value={this.state.value}
     onChange={this.handleChange}
    >
     {projects.length > 0 &&
      projects.map((item, i) => {
       return (
        <option key={i} value={item.id}>
         {item.name}
        </option>
       );
      })}
    </select>
   </div>
  );
 }
}

render(<App />, document.getElementById("root"));

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript中的Split使用方法与技巧
Mar 09 Javascript
javascript动态创建及删除元素的方法
Dec 22 Javascript
JQuery插件ajaxfileupload.js异步上传文件实例
May 19 Javascript
jQuery时间轴插件使用详解
Jul 16 Javascript
jquery限定文本框只能输入数字(整数和小数)
Jan 08 Javascript
AngularJS入门教程之AngularJS表达式
Apr 18 Javascript
利用JS实现页面删除并重新排序功能
Dec 09 Javascript
Vue 过渡(动画)transition组件案例详解
Jan 22 Javascript
使用 Node.js 实现图片的动态裁切及算法实例代码详解
Sep 29 Javascript
Vue匿名插槽与作用域插槽的合并和覆盖行为
Apr 22 Javascript
vue中$refs, $emit, $on, $once, $off的使用详解
May 26 Javascript
手把手教你实现 Promise的使用方法
Sep 02 Javascript
vue框架下部署上线后刷新报404问题的解决方案(推荐)
Apr 03 #Javascript
JavaScript变速动画函数封装添加任意多个属性
Apr 03 #Javascript
JS中注入eval, Function等系统函数截获动态代码
Apr 03 #Javascript
性能优化篇之Webpack构建速度优化的建议
Apr 03 #Javascript
elementUI多选框反选的实现代码
Apr 03 #Javascript
vue生命周期的探索
Apr 03 #Javascript
用原生 JS 实现 innerHTML 功能实例详解
Apr 03 #Javascript
You might like
漫荒推荐:画风超赞的国风漫画推荐 超长假期不无聊
2020/03/08 国漫
PHP中用正则表达式清除字符串的空白
2011/01/17 PHP
PHP SEO优化之URL优化方法
2011/04/21 PHP
如何在smarty中增加类似foreach的功能自动加载数据
2013/06/26 PHP
php将字符串转换成16进制的方法
2015/03/17 PHP
yii2.0整合阿里云oss删除单个文件的方法
2017/09/19 PHP
PHP从尾到头打印链表实例讲解
2018/09/27 PHP
PHP实现数组和对象的相互转换操作示例
2019/03/20 PHP
javascript据option的value值快速设定初始的selected选项
2007/08/13 Javascript
通过jQuery打造支持汉字,拼音,英文快速定位查询的超级select插件
2010/06/18 Javascript
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
2010/11/09 Javascript
jQuery 计算iframe 窗口大小的方法
2014/05/13 Javascript
AngularJS基础 ng-class-odd 指令示例
2016/08/01 Javascript
酷! 不同风格页面布局幻灯片特效js实现
2021/02/19 Javascript
javascript设计模式之单体模式学习笔记
2017/02/15 Javascript
Nodejs基于LRU算法实现的缓存处理操作示例
2017/03/17 NodeJs
jQuery UI Grid 模态框中的表格实例代码
2017/04/01 jQuery
原生JS实现$.param() 函数的方法
2018/08/10 Javascript
nodejs同步调用获取mysql数据时遇到的大坑
2019/03/02 NodeJs
jQuery动态生成的元素绑定事件操作实例分析
2019/05/04 jQuery
微信小程序如何使用云开发
2019/05/17 Javascript
jQuery提示框插件SweetAlert用法分析
2019/08/05 jQuery
vue项目配置同一局域网可使用ip访问的操作
2020/10/23 Javascript
JavaScript canvas实现跟随鼠标移动小球
2021/02/09 Javascript
[52:03]DOTA2-DPC中国联赛 正赛 Ehome vs iG BO3 第三场 1月31日
2021/03/11 DOTA
python的id()函数介绍
2013/02/10 Python
python查找指定具有相同内容文件的方法
2015/06/28 Python
Python中偏函数用法示例
2018/06/07 Python
解决python中的幂函数、指数函数问题
2019/11/25 Python
python 将dicom图片转换成jpg图片的实例
2020/01/13 Python
匡威德国官网:Converse德国
2019/01/26 全球购物
电脑售后服务承诺书
2014/03/27 职场文书
《猴子种果树》教学反思
2014/04/26 职场文书
2014年校长工作总结
2014/12/11 职场文书
2015新学期校长寄语(3篇)
2015/03/25 职场文书
Python中相见恨晚的技巧
2021/04/13 Python