前端策略模式:react hooks 表单验证

发布日期:2023-07-17 阅读(0) 评论(0) 赞(0)
react hooks 表单验证—策略模式 1.前置知识概述 策略模式的定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,策略模式的目的就是将算法的使用与算法的实现分离开来,避免使用多重条件判断。 策略类封装了具…

react hooks 表单验证—策略模式

1.前置知识概述

  • 策略模式的定义

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,策略模式的目的就是将算法的使用与算法的实现分离开来,避免使用多重条件判断
策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context 接受客户的请求,随后把请求委托给某一个策略类。

2.背景

  • 在日常的需求开发中避免不了各种表单的校验,非空,长度,最大最小值等等,可能很多同学为了编写方便会通过多个if…else 来判断不同的校验逻辑。
  • 比如现在提一个简单的需求,需要用户输入姓名、年龄、日期,姓名不能为空并且长度不能超过10,年龄不能为空,日期可以为空但能超过当前时间。如果校验失败需要toast提示,(注:姓名如果校验失败需要将label标红)

3.校验器的功能

  • 开始编码之前需要清楚的是,我们预期它有什么功能,知道需要什么才能去设计去开发。很多同学写程序没有思路就在于没有想好自己想要的功能,或者使用方式。

  • 需要实现的功能
    1.支持添加单个规则,多个对规则
    2.支持对检测到的非法值做出响应

  • 使用方式:

// 对属性只加多个校验规则 对name的值进行校验 校验是否为空 长度是否超过10
validator.add(formDate.name, [{type: TYPES.isEmpty,rules: {errorMsg: '姓名不能为空',errCallBack: (errMsg) => {console.log(errMsg, '校验失败的回调 处理name的label变红色')}}},{type: TYPES.maxLength,rules: {errorMsg: '姓名长度不能超过10',maxlength: 10}}
]);// 对属性值加单个校验规则 校验age是否为空
validator.add(formDate.age, [{type: TYPES.isEmpty,rules: {errorMsg: '年龄不能为空'}}
]);// 开始校验
const errorMsg = validator.start();// 判断是否通过校验
if (errorMsg) {console.log(errorMsg, '未通过校验')
}

4.实现一个校验器

4.1 封装多种校验策略

  • 可根据具体的业务需求封装同的校验规则,主要的校验思路就是通过传入被校验的值 和校验它所需要的参数进行判断,是否合法。这里需要什么参数完全由业务需求而定
  • 比如我们要判断长度,那就需要maxlength指定一个期望的长度,要判断日期是否大于某一天,那就需要maxDate指定不能超过的日期值。
  • 所以具体情况具体分析,当有新的校验规则时,只需要新增策略即可。
// 所有的校验规则
const strategies = {// 空值校验isEmpty: (value, rules) => {// rules使用对象传参 可以使得不同规则下定义不同的参数const {errorMsg, errCallBack} = rules;if (!value && value !== 0) {errCallBack && errCallBack(errorMsg);return errorMsg;}},// 长度校验maxLength: (value, rules) => {const {maxlength, errorMsg, errCallBack} = rules;if (value.length > maxlength) {errCallBack &&  errCallBack(errorMsg);return errorMsg;}},// 大于指定时间校验gtDate: (value, rules) => {const {maxDate, errorMsg, errCallBack} = rules;const dateVal = new Date(value);const dateMax = new Date(maxDate);if (dateVal > dateMax) {errCallBack &&  errCallBack(errorMsg);return errorMsg;}}
};

4.2 实现校验器类

  • 帮我们承接校验的逻辑,作为一个校验的入口
  • 主要就是实现一个add,和一个start方法,add用来找到对应的规则函数传入对应参数缓存到队列中等待被执行;start用来遍历当前的规则校验队列。
// 校验器
class Validator {constructor() {// 用来存储校验策略的队列this.cach = [];}// 支持添加多个和单个add (value, rulesArgs = []) {// 兼容单个规则的写法if (!(rulesArgs instanceof Array)) {rulesArgs = [rulesArgs];}rulesArgs.forEach(ruleArg => {const {type , rules} = ruleArg;this.cach.push(() => {// 返回对应规则策略的执行return strategies[type] && strategies[type](value, rules);});})}// 开始校验start () {// 根据add的顺序逐一校验for (const strategyFn of this.cach) {const msg = strategyFn();// 遇到非法结果 退出循环if (msg) {return msg;}}}
}export const validator = () => {return new Validator();
}

5.结合react体验一下吧

  • 效果如下:
    请添加图片描述
  • 在submit提交的时候,我们只需要将校验的逻辑收敛到validateForm中,不需要其在提交时进行各种校验的分支逻辑,方便维护,条例清晰。
import styles from './index.module.less'; 
import { Input, Form, Button, Toast, DatePicker } from 'antd-mobile';
import { useState } from 'react';
import moment from 'moment';import { validator as getValidator, TYPES } from '../tools/v';// 方便多个类名的书写
const classNames = (classNames) => {let result = '';classNames.forEach(name => {if (styles[name]) {result += ` ${styles[name]}`;}});return result;
}function Test() {// 表单数据const [formData, setformData] = useState({age: '',name: '',date: ''});// 针对某个数据的校验做特殊样式处理const [formErr, setFormErr] = useState({name: false});// 控制时间选择器的显示const [visible, setVisible] = useState(false);const toast = (msg) => {Toast.show({content: msg});}// 校验表单数据const validateForm = (formData) => {const validator = getValidator();const {age, name, date} = formData;// 添加校验规则validator.add(name, [{type: TYPES.isEmpty,rules: {errorMsg: '姓名不能为空',// 一些定制化的错误处理errCallBack: () => {setFormErr(state => ({...state, name: true}));}}}, {type: TYPES.maxLength,rules: {maxlength: 10,errorMsg: '姓名不得超过10个字',errCallBack: () => {setFormErr(state => ({...state, name: true}));}}}]);// 校验年龄validator.add(age, {type: TYPES.isEmpty,rules: {errorMsg: '年龄不能为空'}});// 校验日期validator.add(date, {type: TYPES.gtDate,rules: {errorMsg: '日期不能大与当前时间',maxDate: moment(new Date()).format('YYYY-MM-DD')}});const errMsg = validator.start();// 统一处理toastif (errMsg) {toast(errMsg);return true;}}// 提交表单const submit = () => {const isIllegal = validateForm(formData);if (isIllegal) {return;}toast('提交成功');}return (<div className={styles.wrap}><Formlayout='horizontal'footer={<Button block type='submit' color='primary' size='large' onClick={submit}>提交</Button>}mode='card'><Form.ItemclassName={classNames(['name', formErr.name ? 'error' : '']) }label='姓名'><InputmaxLength='20'placeholder='请输入姓名'value={formData.name}onChange={val => {setFormErr(state => ({...state, name: false}))setformData(state => ({...state, name: val}))}}/></Form.Item><Form.Itemlabel='年龄'><Inputplaceholder='请输入年龄'max={120}type='number'value={formData.age}onChange={val => {setformData(state => ({...state, age: val}))}}/></Form.Item><Form.Item><Button onClick={() => setVisible(true)}>{formData.date ? formData.date : '时间选择'}</Button></Form.Item></Form><DatePickertitle='时间选择'visible={visible}onClose={() => {setVisible(false);}}onConfirm={val => {setformData(state => ({...state, date: moment(val).format('YYYY-MM-DD')}))}}/></div>);
}export default Test;

6.总结

  • 平时学习设计模式,可能感觉很难应用到业务开发中,又很少封装一些库或工具,后面我要会继续结合一些设计模式,应用到实际的业务开发场景,一起学习。
  • 以上的代码肯定还是不够优雅的,仅仅为了给同学们提供一个思路和方向,本人也是借鉴JavaScript设计模式与开发实践中的案例进行一些调整和完善。
  • 如有错误的地方请大家及时指出,比卖你误导其他同学~
相关文章
    最新文章
    热门标签