React 使用Hooks简化受控组件的状态绑定
开始之前
阅读本文需要对以下几项有一定了解
ECMAScript6
文章中大量用到了ES6语法,比如解构赋值和函数参数默认值、剩余参数、展开语法、箭头函数等。
Hooks
React在16.8版本中推出了Hooks,它允许你在“函数组件”中使用“类组件”的一些特性。
React本身提供了一些Hooks,比如useState、useReducer等。通过在一个以“use”作为命名起始的函数中调用这些Hooks,就得到了一个customHook(自定义Hook)。
CustomHooks允许我们把任何逻辑封装到其中,以便于复用足够小的组件逻辑。
ControlledComponents
当我们把像
styled-components
一个与React契合良好的CSSinJS库。它允许你使用JS编写样式,并编译成纯CSS文件。
下面代码中所有的样式都是使用它编写的。如果对代码中样式的实现不是很感兴趣的话,这个可以跳过。
代码实现
Input组件
首先我们需要实现一个Input组件,我们将在该组件的基础上进行输入、校验并提示。
Input.js
importReactfrom'react'; importPropTypesfrom'prop-types'; importstyledfrom'styled-components'; constWrap=styled.div({ display:'flex', flexDirection:'column', label:{display:'flex',alignItems:'center'}, input:{ marginLeft:8, }, p:{ color:'red', }, }); functionInput({label,type,helperText,error,...otherProps}){ return({error&& ); } Input.propTypes={ label:PropTypes.string, type:PropTypes.string, helperText:PropTypes.string, error:PropTypes.bool, }; exportdefaultInput;{helperText}
}
该组件主要接收以下几个props:
- labellabel标签的文本
- type赋值给原生input标签的type属性
- error数据类型为Boolean,如果为true则表示当前表单域有错误,即验证不通过
- helperText当前表单域验证不通过时,显示在表单域下方的提示文字
- otherPropsprops中除了上述四个以外的其他属性,全部赋值给原生input标签
CustomHook
有了UI组件之后,就可以开始实现我们的自定义Hook了。
useInput.js
import{useState}from'react'; exportdefaultfunctionuseInput({ initValue='', helperText='', validator=()=>true, validateTriggers=['onChange'], }={}){ //保存用户输入的值,使用initValue作为初始值 const[value,setValue]=useState(initValue); //Boolean类型,表示当前表单项的验证状态 const[error,setError]=useState(false); functiononChange(e){ const{value}=e.target; setValue(value); //根据validateTriggers的选项,决定是否要在onChange里进行校验 if(validateTriggers.includes('onChange')){ setError(!validator(value)); } } /** *根据validateTriggers生成相应的事件处理器 */ functioncreateEventHandlers(){ consteventHandlers={}; validateTriggers.forEach(item=>{ //生成相应的事件处理器,并在其中做输入校验。 eventHandlers[item]=e=>{ const{value}=e.target; setError(!validator(value)); }; }); returneventHandlers; } consteventHandlers=createEventHandlers(); return{ value, helperText, error, ...eventHandlers, onChange, }; }
useInput接收一个options对象作为参数,考虑到扩展性,使用一个配置对象作为参数比较好。
options对象拥有以下几个属性:
- initValue输入框的初始值
- helperText当表单验证不通过时显示的字符串
- validator用于进行表单验证的函数,接收value作为参数,并返回一个Boolean值,表示表单验证是否通过
- validateTriggers字符串数组,表明在哪个或哪几个事件中调用validator进行输入校验。
在函数体中,我们调用两次useState来初始化value和error的值,分别保存用户输入的值和当前表单域的校验结果。
然后,声明一个onChange方法用来绑定input元素的change事件,在该方法中,我们把用户输入的值赋值给value,同时根据validateTriggers的值,决定是否要在该方法中进行输入校验。该方法随后会被返回出去,再作为props传递给相应的组件,完成受控组件的状态绑定。
我们还需要声明一个createEventHandlers方法,该方法通过遍历validateTriggers,生成相应的事件处理器,并在这些事件处理器中进行输入校验。
最后我们调用createEventHandlers方法,并把生成的eventHandlers(事件处理器)通过扩展运算符,插入到最终返回的对象中。
注意:这里我们需要把onChange放在最后,以免带有状态绑定的onChange方法被eventHandlers中的onChange覆盖掉。
具体使用
现在让我们来看看实际该如何使用:
importReactfrom'react'; importInputfrom'./Input'; importuseInputfrom'./useInput'; //用于验证邮箱的正则表达式 constEMAIL_REG=/\S+@\S+\.\S+/; exportdefaultfunctionForm(){ constemail=useInput({ initValue:'', helperText:'请输入有效的邮箱!', validator:value=>EMAIL_REG.test(value), validateTriggers:['onBlur'], }); constpassword=useInput({ initValue:'', helperText:'密码长度需要在6-20之间!', validator:value=>value.length>=6&&value.length<=20, validateTriggers:['onChange','onBlur'], }); /** *判断是否禁用按钮 */ functionisButtonDisabled(){ //当邮箱或密码未填写时,或者邮箱或密码输入校验未通过时,禁用按钮 return!email.value||!password.value||email.error||password.error; } /** *处理表单提交 */ functionhandleButtonClick(){ console.log('邮箱:',email.value); console.log('密码:',password.value); } return(); }登录
这里调用了两次useInput,初始化email和password表单域数据。
然后使用扩展运算符,把值全部赋给Input组件。只用了几行代码就完成了定义初始值和受控组件的绑定,是不是很方便?
当我们输入邮箱的时候,并不会出现校验提示,但是一旦从邮箱输入框失去焦点以后,输入的值就会被校验,并根据校验结果显示相应的提示。而密码输入框,则会在输入的过程中和失焦后都进行校验。
总结
上面这个例子已经可以处理基本的表单验证,至于格式化用户输入的数据以及自定义收集表单域的值的时机等其他需求,我就不再演示了,大家可以自行设计。这也是Hooks的特殊之处,它让我们可以更容易的复用逻辑代码,可以根据需要自行编写customHooks。
文章中关于useInput的API设计只是众多方案中的一种,只是为大家提供一些参考。你也可以把整个表单的状态封装到一个useForm方法中,统一管理所有表单域的状态。
希望本文能为大家带来一些关于如何使用Hooks的灵感,即使从来没有使用过Hooks,也强烈建议大家尝试一下。我已经在项目中大量使用Hooks了,并且它也为我带来了很好的效果。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。