NutUI-React Input输入框的使用指南
NutUI 是一套京东风格的轻量级移动端组件库,覆盖移动端主流业务场景。目前已支持 Vue 和 React 两大框架。另外我们还启动多端小程序适配,选用 Taro 基础框架作为底层实现,经过半年的打磨,目前已完成了所有组件的适配,可以为开发者提供便捷的多端小程序能力。
随着 NutUI-React 上线以来,我们听到了许多好的声音,同时也收获了很多中肯的建议和一些问题反馈。在众多的反馈中,用户对输入框Input组件的使用率较高,同时反馈的问题也比较多,本次我们就将展开说说 Input 输入框组件。
功能分析
在web的世界要实现人机交流,传递文字、图片、视频、语言等信息,最基本的就是输入信息。那么输入框要怎么实现“输入”的功能呢?
听起来“输入”这个功能好像很简单,但要考虑的事情却很多。在输入前我们要告诉用户是否要输入、在哪里输入、输入什么内容、内容要满足什么条件等;在输入时又要关注用户输入内容是否要校验,输入的交互体验等;完成输入操作后,我们是否要对输入内容做反馈,是否提示用户可清除内容等等。
在调研了市面上的移动端 Input 组件之后,我们确定了 Input 组件的大致功能如下:
总体分为五类功能:
- 基本功能类,实现输入框的输入功能、输入类型
- 样式类,实现输入框文本对齐、有无图标、有无边框等样式设置
- 提示类,实现输入内容是否必填、是否有误、是否显示错误文案
- 自定义类,实现用户自定义按钮、自定义输入框、是否校验文本内容等功能
- 事件,实现用户点击组件及组件各图标的回调事件
组件的实现
基本功能实现
基础功能的实现都是基于原生 Input 输入框的自带属性,在这些基础属性上做了扩展,首先我们先来看看组件的整体布局。
<div className={`${classes} ${className || ''}`}
style={style}
{...rest}
onClick={(e) => {
onClick && onClick(e)
}}>
{slotInput?(...)//用户自定义input插槽
:(<>
{leftIcon ...} //左侧图标
{label ...} //输入框标题
<div className="nut-input-value">
<div className="nut-input-inner" onClick={(e) => { handleClickInput(e)}}>
{type === 'textarea' ?(<textarea />)
:(<input
name={name}
className="input-text"
ref={inputRef}
style={{ textAlign: inputAlign }}//输入框中文本对齐方式inputAlign
type={inputType(type)} //输入类型
maxLength={maxlength} //输入框最大字数限制
placeholder={placeholder || locale.placeholder} //占位符
disabled={disabled} //禁用
readOnly={readonly} //只读
value={inputValue} //对应props中初始默认值defaultValue
autoFocus={autofocus}
onBlur={(e: any) => {handleBlur(e)}}
onFocus={(e: any) => {handleFocus(e)}}
onInput={(e: any) => {handleInput(e)}}
/>)}
{clearable ... } //清除按钮
</div>
{rightIcon ...} //右侧图标
{slotButton ...} //用户自定义按钮插槽
{showWordLimit && maxlength ...} //字数统计
{errorMessage ...} //错误提示
</div>
</>)}
</div>
以上代码能看出整个 Input 输入框的布局结构,基本上原生组件上有的参数和事件我都做了绑定和露出,实现了输入功能、自定义输入类型、禁用、只读和回调事件,同时也对输入框设置了 style 样式属性,实现了的文本对齐方式 inputAlign。其他扩展的功能,在下文中会有侧重的提及,如果感兴趣原码的可以去 github 中查阅。
提示类功能实现
提示类功能比如清除按钮提示、错误文案提示、字数限制提示和必填提示的实现也相对简单,我们看下清除按钮和错误消息提示的代码。
//清除按钮显示
{clearable && !readonly && active && inputValue.length > 0 ? (
<Icon
classPrefix={iconClassPrefix}
fontClassName={iconFontClassName}
className="nut-input-clear"
name={clearIcon}
size={clearSize}
onClick={(e) => {handleClear(e)}}
/>
) : null}
清除按钮的代码逻辑中给了一些限制条件,只有在设置了 clearable 为 true 时,且不是只读,输入框 focus 状态,并且输入框有文字输入时才给出清除按钮的提示。清除按钮图标是引用了 Icon 组件,可以支持图标的替换和尺寸的修改,具体使用方法可以参见 NutUI-React 的 Icon 组件。
//错误消息提示
{errorMessage ? (
<div
className="nut-input-error-message"
style={{ textAlign: errorMessageAlign}}
>
{errorMessage}
</div>
) : null}
从代码可以看出,错误消息提示需要设置 errorMesge 属性的文案才能出现提示,错误提示还提供了提示文案位置的配置,具体使用方法如下。
//清除按钮
<Input
label="文本"
placeholder="必填项"
required
maxlength={10}
clearable
clearIcon="del"
clearSize="20"
/>
const [errorState, setError] = useState(false)
const [firstFlag, setFirstFlag] = useState(true)
{
setFirstFlag(false)
}}
onChange={(value) => {
if (value.length === 0 && !firstFlag) {
setError(true)
} else {
setError(false)
}
}}
required
error={errorState}
errorMessage={errorState ? '不能为空提示' : ''}
/>
上面两个例子对输入框设置了必填项提示、清除按钮提示、字数显示和错误文案的提示,错误提示的非空校验也运用 onChange 和 onFocus 事件做了个小案例,具体效果如下:
看完例子是不是对 Input 输入框提示类功能有了更清晰的了解呢?接下来我们来看看 Input 的自定义的功能。
自定义功能实现
在 NutUI-React 的 Input 组件中,我们还为用户扩展了几个自定义的属性:
输入格式校验
当用户设置参数 formatter规则和 formatterTrigger 时决定了用户输入的内容将经过正则校验。
我们会根据 formatterTrigger 设置的触发事件 onChange 或 onBlur 来决定执行校验的时机,校验规则是用 formatter 中所设置的规则,具体使用方法如下。
const formatter = (value: string) => value.replace(/\d/g, '')
<Input
label="文本"
placeholder="在输入时执行格式化"
formatter={formatter}
/>
<Input
label="文本"
placeholder="在失焦时执行格式化"
formatter={formatter}
formatTrigger="onBlur"
/>
上述例子中,我们设置了 formatter 校验规则为不能为数字,当我们不设置 formatTrigger 时默认走的是 onChange 校验时机,即实时输入校验。当设置 formatTrigger 为 onBlur 时则是走的失焦后校验。
文本输入框模式
Input 组件除了使用了 Input 原生输入框,我们还为用户提供了文本框 Textarea 组件,满足了用户操作多行文本的需求,我们先来看下代码。
{type === 'textarea' ? (
<textarea
name={name}
className="input-text"
ref={inputRef}
style={{
textAlign: inputAlign,
height: `${Number(rows) * 24}px`,
}}
maxLength={maxlength}
placeholder={placeholder || locale.placeholder}
disabled={disabled}
readOnly={readonly}
value={inputValue}
autoFocus={autofocus}
onBlur={(e: any) => {handleBlur(e)}}
onFocus={(e: any) => {handleFocus(e)}}
onInput={(e: any) => {handleInput(e)}}
/>
): (<input ... />)
}
当用户设置了 type 为 textarea 时,代码中就是使用 textarea 原生组件,这里绑定的属性与 Input 功能基本一致,只是在 style 属性上增加了行数的设置,用户可以通过 rows 设置文本的高度,具体使用如下。
<Input
label='留言'
placeholder='留言'
defaultValue=''
type="textarea"
rows="2"
maxlength="50"
/>
事件的实现
事件的实现其实从上边各功能分析就能看出是如何实现的,除了 Input 和 Textarea 的原生事件,我们在各个图标 Icon 上也做了点击事件的绑定,方便用户使用。具体事件调用可以查看官方文档。
Issue
自 NutUI-React Input 上线以来,我们也收到了一些用户的Issue反馈,这里我们挑了一些比较典型的跟大家聊聊。
- input 双向绑定问题 (Issue #355)
React 与 Vue 不同,它是没有双向绑定的概念的,因为它的设计思想就是单向数据流。但是它也是可以通过改变 state 来实现双向数据绑定的功能。
const [value, updateValue] = useState('')
<Input
name="text"
label='text'
placeholder='text'
defaultValue={value}
onChange={(val) => {
updateValue(val)
}}
/>
上边的例子就实现了一个数据双向绑定的功能,当用户输入内容触发 onChange 事件时会通过 updateValue 来改变 value 的值,从而实现双向绑定。
- Input defaultValue无法清空 (Issue #446)
这个问题是用户在清除组件 value 也就是 defaultValue 时,发现不能置空。经过排差是因为在设计之初,考虑到用户输入的文字时才更新输入框内容,导致 defaultValue 为空时输入框不能被更新。于是我们放开了输入限制,只要改变输入内容就会被更新。
这个问题也让我注意到在设计组件时要考虑使用场景的全面性,开发完之后要实际使用下每个属性的功能。
- Input 实际与文档不符(Issue #355)
这个问题是因为多次改版升级和功能迭代引起的遗漏问题,为此我们做了文档优化和事件统一的工作,将功能补齐和文档规范化。
小结
希望本篇文章能对未使用过或使用过程中遇到问题的朋友们有所帮助,也感谢社区的小伙伴对 Input 组件的使用和问题反馈,我们还有诸多不足,需要不断地打磨,为了实现“好用的” Input 输入框,接下来我们还有许多待优化的点要跟进。期待我们更好的 NutUI-React Input 输入框吧。
联系我们
欢迎添加微信群,添加 hanyuxinting,回复 React,邀请进群。 我们的 Github 地址、 NPM 包地址