網站首頁 編程語言 正文
Situation 背景
- 多人開發的老項目里面,很多地方都寫了驗證手機格式的需求,代碼各有千秋、百花齊放
- 實現:有的寫在公共組件庫里,有的是單獨開發局部組件支持,有的直接手寫不復用,有的抽離正則到utils再引入
- 正則:正則校驗也各有千秋,比如/^\d{11}/、/1\d10/、/1[2?9]\d9/、/^1\d{10}/、/^1[2-9]\d{9}/、/1\d10/、/1[2?9]\d9/、/^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/等等。
- 長度限制:有的地方限制maxLength=11,而有的地方沒限制
- 輸入格式:有的地方滿足334格式(181 2222 3333)手機號碼輸入, 而有的地方只允許輸入純數字
Target 目標
- 實現一個易用的手機號碼公共組件
- 使用較為寬松的正則校驗并與后端達成一致
- 不再限制輸入長度但會報錯提示
- 支持334的格式輸入,并自動去除所有空格
- 寫好參數說明,方便開發使用
- 盡可能的保持靈活,適用更多場景
Action 行動
import type {
FormItemProps,
InputProps,
RulesProps,
} from '@arco-design/web-react';
import { Form, Input } from '@arco-design/web-react';
import { usePersistCallback } from '@byted/hooks';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
export type VerifyInputProps = {
/** ?? 綁定屬性 */
field: string;
/** ?? 顯示標簽文字 */
label: string;
/** 標簽前面是否顯示必填*號 */
labelRequired?: boolean;
/** ?? rules是否加入必填校驗(為true時無需再設置labelRequired=true) */
phoneRequired?: boolean;
/** 除檢驗手機號格式外其他規則,默認添加正則檢驗/^1[2-9]\d{9}$/ */
rules?: RulesProps<any>[];
/** 顯示提示文字 */
placeholder?: string;
/** 是否禁用input */
disabled?: boolean;
/** phone輸入框onChange事件 */
onChange?: InputProps['onChange'];
/** FormItem其他屬性 */
formItemProps?: FormItemProps;
/** Input其他屬性 */
inputProps?: InputProps;
};
/** 手機號表單(帶有 Form.Item) */
export const VerifyPhoneInput = (props: VerifyInputProps) => {
const {
field,
label,
placeholder,
rules = [],
labelRequired = false,
phoneRequired = false,
disabled,
onChange,
formItemProps,
inputProps,
} = props;
const resultRules = useMemo(() => {
const arr = [
{
validator(val: string | undefined, cb: (error?: ReactNode) => void) {
const realVal = (`${val}` as any).replaceAll(' ', '');
if (!val || /^1[2-9]\d{9}$/.test(realVal)) {
cb();
} else {
cb(`請輸入正確的${label}`);
}
},
},
...rules,
];
if (phoneRequired) {
arr.unshift({
validator(val: string | undefined, cb: (error?: ReactNode) => void) {
if (!val) {
cb(`請輸入${label}`);
} else {
cb();
}
},
});
}
return arr;
}, [rules, phoneRequired, label]);
const normalize = usePersistCallback((val: string | undefined) => {
if (val) {
const realVal = (`${val}` as any).replaceAll(' ', '');
if (val !== realVal) {
return realVal;
}
}
return val;
});
const resultInputProps = {
disabled,
onChange,
allowClear: true,
placeholder: placeholder || `請輸入${label}`,
...inputProps,
};
return (
<Form.Item
required={labelRequired || phoneRequired}
field={field}
label={label}
formatter={normalize}
rules={resultRules}
{...formItemProps}
>
<Input {...resultInputProps} />
</Form.Item>
);
};
- 定義入參類型VerifyInputProps,并按使用頻率對屬性排序并加以說明
- 將是否必填從rules中移出變成phoneRequired參數,錯誤提示也根據label自動拼接請輸入正確的${label},封裝復雜、暴露簡單
- placeholder默認根據label自動拼接成(請輸入${label}),提高易用性
- 將FormItem其他屬性收斂到formItemProps中,方便開發同學擴展,提高通用性
- 默認allowClear=true,可以通過inputProps進行覆蓋
- 通過formatter自動去掉所有空格,支持334格式
- 將Input其他屬性收斂到inputProps中,方便開發同學擴展,提高通用性
- 暴露rules,支持自定義遠程驗證等規則
- 暴露常用的disabled、onChange在最外層方便使用
Result 結果
- 已經投入使用,測試符合預期,提供給其他同事使用,后續繼續完善
Review 復盤
- 問題1:使用normalize去空格后,input下面的錯誤驗證并沒有重新觸發導致輸入框數據已經是對的,而錯誤提示還存在的現象。
- 解決:第一種方案將form傳入,在formatter之后通過form?.validate(field)來重新觸發驗證;第二種方案將rules改為validator的實現方式手動去空格。不希望引入更多參數,最終采用第二種。
- 問題2:正則格式到底怎么定義才能平衡正確性和普適性
- 解決:和后端協商使用適用目前所有可見場景下的最嚴格的驗證格式/^1[2-9]\d{9}$/
- 問題3:老代碼治理
- 解決:一次性將所有手機號碼相關的代碼全部改掉,需要測試介入帶來大量的回歸測試時間,而收益很小。所以新需求使用此組件,老需求改動的時候再改成此組件的方案代價更小。
- 問題4:還能再豐富一哈嗎?
- 支持151****2222的顯示與編輯時未修改不校驗,保證手機號私密性
- 問題5:VerifyPhoneInput實際是個FormItem組件,且api與FormItem原本的有很大區別,會不會給使用者帶來心智負擔?
- 解決:
import type { FormItemProps, InputProps } from '@arco-design/web-react';
import { Form, Input } from '@arco-design/web-react';
import { usePersistCallback } from '@byted/hooks';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
const defaultLabel = '手機號碼';
export type PhoneFormItemProps = {
/** ?? rules是否加入必填校驗(為true時無需再設置labelRequired=true) */
phoneRequired?: boolean;
/** ?? rules是否加入正則校驗(/^1[2-9]\d{9}$/),默認true */
phoneFormatCheck?: boolean;
/** Input其他屬性 */
inputProps?: InputProps;
} & FormItemProps;
/** 手機號表單(帶有 Form.Item) */
export const PhoneFormItem = (props: PhoneFormItemProps) => {
const {
phoneRequired = false,
phoneFormatCheck = true,
inputProps,
...formItemProps
} = props;
const resultRules = useMemo(() => {
const arr = [...(formItemProps.rules || [])];
if (phoneFormatCheck) {
arr.unshift({
validator(val: string | undefined, cb: (error?: ReactNode) => void) {
const realVal = (`${val}` as any).replaceAll(' ', '');
if (!val || /^1[2-9]\d{9}$/.test(realVal)) {
cb();
} else {
cb(`請輸入正確的${formItemProps?.label || defaultLabel}`);
}
},
});
}
if (phoneRequired) {
arr.unshift({
validator(val: string | undefined, cb: (error?: ReactNode) => void) {
if (!val) {
cb(`請輸入${formItemProps.label || defaultLabel}`);
} else {
cb();
}
},
});
}
return arr;
}, [
formItemProps.rules,
formItemProps.label,
phoneFormatCheck,
phoneRequired,
]);
const normalize = usePersistCallback((val: string | undefined) => {
if (val) {
const realVal = (`${val}` as any).replaceAll(' ', '');
if (val !== realVal) {
return realVal;
}
}
return val;
});
const resultInputProps = {
allowClear: true,
...inputProps,
placeholder:
inputProps?.placeholder || `請輸入${formItemProps.label || defaultLabel}`,
};
return (
<Form.Item
normalize={normalize}
rules={resultRules}
{...formItemProps}
required={formItemProps.required || phoneRequired}
>
<Input {...resultInputProps} />
</Form.Item>
);
};
以前都是用vue,來字節之后天天react,感覺自己變菜了好多,又或許我原本就菜的離譜。
原文鏈接:https://juejin.cn/post/7132310843689009160
相關推薦
- 2022-03-03 el-form動態表單切換導致的校驗bug(v-if、v-show導致校驗失效的bug)
- 2023-07-16 springboot動態端口
- 2022-03-26 C語言的字符函數和字符串函數詳解_C 語言
- 2022-07-12 k8s conntrack 表項超時導致tcp長連接中斷
- 2022-03-26 C++中靜態數據成員使用示例_C 語言
- 2022-12-30 React錯誤邊界Error?Boundaries詳解_React
- 2022-04-06 Go?字符串比較的實現示例_Golang
- 2022-06-21 Git基礎之git與SVN版本控制優缺點區別分析_其它綜合
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支