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 相关文章推荐
延时重复执行函数 lLoopRun.js
May 08 Javascript
用jQuery简化JavaScript开发分析
Feb 19 Javascript
仅IE6/7/8中innerHTML返回值忽略英文空格的问题
Apr 07 Javascript
javascript中的数字与字符串相加实例分析
Aug 14 Javascript
js获取php变量的实现代码
Aug 10 Javascript
jquery实现可旋转可拖拽的文字效果代码
Jan 27 Javascript
javascript事件模型介绍
May 31 Javascript
浅谈jQuery操作类数组的工具方法
Dec 23 Javascript
vue子组件使用自定义事件向父组件传递数据
May 27 Javascript
JS实现百度搜索接口及链接功能实例代码
Feb 02 Javascript
微信小程序实现吸顶特效
Jan 08 Javascript
vue实现循环滚动列表
Jun 30 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
异世界新番又来了,同样是从零开始,男主的年龄降到5岁
2020/04/09 日漫
php 来访国内外IP判断代码并实现页面跳转
2009/12/18 PHP
PHP解密Unicode及Escape加密字符串
2015/05/17 PHP
Joomla框架实现字符串截取的方法示例
2017/07/18 PHP
php使用json-schema模块实现json校验示例
2019/09/28 PHP
laravel-admin自动生成模块,及相关基础配置方法
2019/10/08 PHP
goto语法在PHP中的使用教程
2020/09/17 PHP
js兼容标准的表格变色效果
2008/06/28 Javascript
js根据鼠标移动速度背景图片自动旋转的方法
2015/02/28 Javascript
JavaScript实现自动变换表格边框颜色
2015/05/08 Javascript
jQuery移动页面开发中的触摸事件与虚拟鼠标事件简介
2015/12/03 Javascript
基于javascript实现图片滑动效果
2016/05/07 Javascript
JS中对数组元素进行增删改移的方法总结
2016/12/15 Javascript
Vue 2.0中生命周期与钩子函数的一些理解
2017/05/09 Javascript
微信JSAPI Ticket接口签名详解
2020/06/28 Javascript
jquery在启动页面时,自动加载数据的实例
2018/01/22 jQuery
浅谈vue-cli 3.0.x 初体验
2018/04/11 Javascript
基于JS实现带动画效果的流程进度条
2018/06/01 Javascript
js实现秒表计时器
2019/12/16 Javascript
jQuery 判断元素是否存在然后按需加载内容的实现代码
2020/01/16 jQuery
JS如何实现封装列表右滑动删除收藏按钮
2020/07/23 Javascript
python采用getopt解析命令行输入参数实例
2014/09/30 Python
python爬取51job中hr的邮箱
2016/05/14 Python
python学习基础之循环import及import过程
2018/04/22 Python
解决Django migrate不能发现app.models的表问题
2019/08/31 Python
matplotlib 生成的图像中无法显示中文字符的解决方法
2020/06/10 Python
基于PyInstaller各参数的含义说明
2021/03/04 Python
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
创造美妙香氛体验:Aera扩散器和香水
2018/11/25 全球购物
在C中是否有模拟继承等面向对象程序设计特性的好方法
2012/05/22 面试题
CSS代码检查工具stylelint的使用方法详解
2021/03/27 HTML / CSS
村党建工作汇报材料
2014/11/02 职场文书
导游词之太原天龙山
2020/01/02 职场文书
Navicat for MySQL的使用教程详解
2021/05/27 MySQL
nginx结合openssl实现https的方法
2021/07/25 Servers
教你使用Ubuntu搭建DNS服务器
2022/09/23 Servers