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 相关文章推荐
js里的prototype使用示例
Nov 19 Javascript
jQuery实现表头固定效果的实例代码
May 24 Javascript
js实时获取系统当前时间实例代码
Jun 28 Javascript
Jquery的Tabs内容轮换效果实现代码,几行搞定
Feb 12 Javascript
详解Node.js模块间共享数据库连接的方法
May 24 Javascript
JS+CSS3模拟溢出滚动效果
Aug 12 Javascript
图片文字识别(OCR)插件Ocrad.js教程
Nov 26 Javascript
微信小程序 搜索框组件代码实例
Sep 06 Javascript
JS原形与原型链深入详解
May 09 Javascript
微信h5静默和非静默授权获取用户openId的方法和步骤
Jun 08 Javascript
如何在vue中使用百度地图添加自定义覆盖物(水波纹)
Nov 03 Javascript
JavaScript实现滚动加载更多
Dec 27 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/04/09 魔兽争霸
windows xp下安装pear
2006/12/02 PHP
php 验证码实例代码
2010/06/01 PHP
ThinkPHP采用模块和操作分析
2011/04/18 PHP
从零开始学YII2框架(六)高级应用程序模板
2014/08/20 PHP
php使用Header函数,PHP_AUTH_PW和PHP_AUTH_USER做用户验证
2016/05/04 PHP
php版微信小店调用api示例代码
2016/11/12 PHP
PHP版微信小店接口开发实例
2016/11/12 PHP
document对象execCommand的command参数介绍
2006/08/01 Javascript
xss文件页面内容读取(解决)
2010/11/28 Javascript
AJAX分页的代码(后台asp.net)
2011/02/14 Javascript
jQuery的初始化与对象构建之浅析
2011/04/12 Javascript
浅析JavaScript基本类型与引用类型
2014/05/28 Javascript
jQuery实现文本框输入同步的方法
2015/06/20 Javascript
JS实现在状态栏显示打字效果完整实例
2015/11/02 Javascript
详解使用Vue Router导航钩子与Vuex来实现后退状态保存
2017/09/11 Javascript
详解webpack4多入口、多页面项目构建案例
2018/05/25 Javascript
vue 使用自定义指令实现表单校验的方法
2018/08/28 Javascript
详解js加减乘除精确计算
2019/03/19 Javascript
深入理解redux之compose的具体应用
2020/01/12 Javascript
Python UnicodeEncodeError: 'gbk' codec can't encode character 解决方法
2015/04/24 Python
Python实现递归遍历文件夹并删除文件
2016/04/18 Python
Python使用内置json模块解析json格式数据的方法
2017/07/20 Python
python去掉 unicode 字符串前面的u方法
2018/10/21 Python
Python实现二叉树的最小深度的两种方法
2019/09/30 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
2019/11/28 Python
Win下PyInstaller 安装和使用教程
2019/12/25 Python
Python while循环使用else语句代码实例
2020/02/07 Python
a标签下载链接的简单实现
2016/09/13 HTML / CSS
美国批发零售网站:GearXS
2016/07/26 全球购物
女士时装鞋:Chinese Laundry
2018/08/29 全球购物
大学生校园创业计划书
2014/02/08 职场文书
毕业生简历自我评价范文
2014/04/09 职场文书
领导干部“四风”查摆问题个人整改措施
2014/10/28 职场文书
2015年乡镇科普工作总结
2015/05/13 职场文书
工作证明书
2015/06/15 职场文书