如何用react实现一款input组件

2,317 阅读2分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

TIP 👉 不飞则已,一飞冲天;不鸣则已,一鸣惊人。——《史记·滑稽列传》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。

Input 模态对话框

import

import Input from '@/components/Input/Input';

Props

1. value

  • 类型:string | number(必填)
  • 默认值:无
  • 说明:输入框的内容

2. size

  • 类型:string
  • 默认值:无(框高32px)
  • 说明:large(框高40px)small(框高24px)

3. maxLength

  • 类型:string | number
  • 默认值:无
  • 说明:输入框可输入的最大字符数

4. disabled

  • 类型:bool
  • 默认值:无
  • 说明:是否禁用,true禁用,false可用

5. placeholder

  • 类型:string
  • 默认值:无
  • 说明:输入框描述信息

6. prefix

  • 类型:ReactNode
  • 默认值:无
  • 说明:前缀图标
    • 不传,无前缀图标;传节点,自定义前缀图标

7. suffix

  • 类型:ReactNode
  • 默认值:无
  • 说明:后缀图标
    • 不传,无后缀图标;传节点,自定义后缀图标

8. allowClear

  • 类型:bool
  • 默认值:无
  • 说明:是否显示清空图标,true显示,false不显示

9. onChange

  • 类型:func(必填)
  • 默认值:无
  • 说明:输入内容时触发的回调函数

10. onPressEnter

  • 类型:func
  • 默认值:无
  • 说明:回车按键触发的回调函数

11. onBlur

  • 类型:func
  • 默认值:无
  • 说明:输入框失去光标触发的回调函数

让我们来实现一款input组件

import React from 'react';
import { Component, PropTypes } from '../utils/';
import Icon from '../icon';

export default class Input extends Component {
    static defaultProps = {
        prefixCls: 'input',
        type: 'text',
        autoComplete: 'off',
        onChange() { },
        onSearch() { },
        onKeyUp() { },
    }
    constructor(props) {
        super(props);
        this.state = {
            value: props.value,
            placeholder: props.placeholder,
        };
    }
    componentWillReceiveProps(...props) {
        this.setState({
            ...props,
        });
    }
    handleKeyUp(e) {
        const { onSearch, onKeyUp } = this.props;
        if (e.key === 'Enter') {
            onSearch(e, e.target.value);
        }
        onKeyUp(e);
    }
    // Input-Number 等其它组件使用的方法
    focus() {
        (this.input || this.textarea).focus();
    }
    blur() {
        (this.input || this.textarea).blur();
    }
    handleChange(e) {
        const { onChange, length } = this.props;
        let val = e.target.value;
        if (val.length > length) {
            val = val.slice(0, length);
            e.target.value = val;
        }
        this.setState({ value: val });
        onChange(e, val);
    }
    handleClick(type, e) {
        if (this.props[type]) {
            this.props[type](e, this.state.value);
        }
    }
    renderIcon(type) {
        const { prefixCls, leftIcon, rightIcon, onIconClick, onPreIconClick, onIconMouseOut, onPreIconMouseOut, onIconMouseOver, onPreIconMouseOver } = this.props;
        let icons;

        if (type === 'rightIcon' && typeof rightIcon === 'string') icons = rightIcon;
        if (type === 'leftIcon' && typeof leftIcon === 'string') icons = leftIcon;

        const renderIcon = () => {
            if ((typeof leftIcon === 'string' && icons) || (typeof rightIcon === 'string' && icons)) {
                return (
                    <Icon
                        className="input-icon-inner"
                        type={icons}
                        onClick={this.handleClick.bind(this, type === 'icon' ? 'onIconClick' : 'onPreIconClick')}
                        onMouseOver={this.handleClick.bind(this, type === 'icon' ? 'onIconMouseOver' : 'onPreIconMouseOver')}
                        onMouseOut={this.handleClick.bind(this, type === 'icon' ? 'onIconMouseOut' : 'onPreIconMouseOut')}
                    />
                );
            }
            return type === 'rightIcon' ? rightIcon : leftIcon;
        };
        return (
            <div className={this.classNames({
                [`${prefixCls}-icon-left`]: type === 'leftIcon' && leftIcon,
                [`${prefixCls}-icon-right`]: type === 'rightIcon' && rightIcon,
                event: (type === 'leftIcon' && onPreIconClick) ||
                    (type === 'rightIcon' && onIconClick) ||
                    (type === 'leftIcon' && onPreIconMouseOut) ||
                    (type === 'rightIcon' && onIconMouseOut) ||
                    (type === 'leftIcon' && onPreIconMouseOut) ||
                    (type === 'rightIcon' && onIconMouseOver) ||
                    (type === 'leftIcon' && onPreIconMouseOver),
            })}
            >
                {renderIcon()}
            </div>
        );
    }
    render() {
        const { prefixCls, className, style, type, size, length, leftIcon, rightIcon, value,
            onSearch,
            onIconClick,
            onPreIconClick,
            onIconMouseOut,
            onPreIconMouseOut,
            onIconMouseOver,
            onPreIconMouseOver,
            addonBefore,
            addonAfter,
            height,
            ...other
        } = this.props;
        const cls = this.classNames(`${prefixCls}`, className, {
            textarea: type === 'textarea',
            'w-disabled': this.props.disabled,
        });

        if (type === 'textarea') {
            return (
                <textarea
                    className={this.classNames(cls, `${prefixCls}-inner`)}
                    {...other}
                    value={value}
                    placeholder={!value ? this.state.placeholder : ''}
                    ref={(elm) => { this.textarea = elm; }}
                    type={type}
                    style={style}
                    onChange={this.handleChange.bind(this)}
                />
            );
        }

        return (
            <div
                style={style}
                className={this.classNames(cls, {
                    [`${prefixCls}-${size}`]: size,
                    [`${prefixCls}-icon`]: leftIcon || rightIcon,
                    [`${prefixCls}-addon`]: addonBefore || addonAfter,
                })}
            >
                {addonBefore && <span className={`${prefixCls}-addon-before`}>{addonBefore}</span>}
                {leftIcon && this.renderIcon.bind(this)('leftIcon')}
                {rightIcon && this.renderIcon.bind(this)('rightIcon')}
                <input
                    {...other}
                    type={type}
                    ref={(elm) => { this.input = elm; }}
                    className={this.classNames(`${prefixCls}-inner`, {
                        [`${prefixCls}-p-left`]: leftIcon,
                        [`${prefixCls}-p-right`]: rightIcon,
                        'addon-before': addonBefore,
                        'addon-after': addonAfter,
                    })}
                    value={value}
                    placeholder={!value ? this.state.placeholder : ''}
                    onChange={this.handleChange.bind(this)}
                    onKeyUp={this.handleKeyUp.bind(this)}
                    style={{height: height}}
                />
                {addonAfter && <span className={`${prefixCls}-addon-after`}>{addonAfter}</span>}
            </div>
        );
    }
}

Input.propTypes = {
    prefixCls: PropTypes.string,
    type: PropTypes.string,
    autoComplete: PropTypes.string,
    size: PropTypes.oneOf(['large', 'small', 'mini']),
    length: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    rightIcon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
    leftIcon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
    onChange: PropTypes.func,
    onSearch: PropTypes.func,
    onKeyUp: PropTypes.func,
    addonBefore: PropTypes.node,
    addonAfter: PropTypes.node,
};

index.js

import Input from './Input';
import './input.scss';

export default Input;

样式这块就先不放了

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下