视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
react写一个select组件的实现代码
2020-11-27 21:59:24 责编:小采
文档


之前一直用的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组件用法差不多。效果如下:

下面是组件

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"));

下载本文
显示全文
专题