Input 组件的 trim 处理
以下内容主要针对 Ant Design组件库中 Input 组件值前后空格问题进行trim 处理说明,如需进行代码对照,可参考目录下 react19_ts 项目内实际代码,文档与代码会在git上持续补充
git仓库地址:https://gitee.com/xiaoli-account/react19_ts当前项目在开发时,遇到了个简单又复杂的问题,就是 input 组件输入空格(space)字符的情况
发生场景:创建表单数据时,发现两条数据的名称竟相同,名称字段明明是唯一标识
我的解决思路
- 1、使用 input 组件的 onChange 函数,通过 val.trim()进行空格字符的处理。
但是被我放弃了,因为 onChange 会导致无法输入空格,而我的目的是处理字段值的前后部分空格
- 2、使用 input 组件的 onBlur 函数,通过 val.trim()进行空格字符的处理
也被我放弃了,因为当前项目的页面表单太多了,一个个的去修改表单的字段失焦函数,想想我就放弃了
- 3、使用组件库组件 Form.Item 内置的 normalize 属性,此属性作用(组件获取值后进行转换,再放入 Form 中。不支持异步)
经过我的测试使用,发现其作用与 onChange 函数实现的效果相同,同样会导致无法输入空格,说实话想偷懒没成功
- 4、于是我想可不可以找个相同点或共用点,统一的对字段值进行处理,所以我想到了 axios,我能不能在接口请求前的拦截器中,对参数进行遍历找到 string 类型字段进行 trim 处理呢。
不负众望,我仍然放弃了,因为会发生实际请求参数值与页面显示值不同,还有一旦表单字段过多、表单内字段层次太深,会影响与后台接口交互体验
- 5、说实话我真的被这个问题弄的很难受,所以我才会在开篇说我遇到了个简单又复杂的问题,被逼无奈我选择了对 Ant Design 的 Input 组件二次封装
时间紧任务重,矮子里面拔大个,选择了这个方式,虽然仍需大量替换 ☹️,处理好 Antd 的 <Input/> 与 <Input.TextArea/> 写法
组件设计
梳理 antd 的 input 组件所有写法,封装自己的 input 组件并兼容 antd 的 input 组件,减少对页面表单的改动,理想效果是:全局搜索 input 组件将 <Input/> 替换为 <LeeInput/> 我深知做的越多,错的越多,但此方法也算是做的比较少的了,先解决问题,再优化问题,开整 💪!
- 1、整理所需二次封装组件【Input、Password、TextArea、Search、OTP、Group】
- 2、整理 antd 的 input 组件写法,准备兼容
<Input/> 与 <Input.TextArea/>,函数也是对象,本质通过对象进行兼容 <Input.TextArea/> 写法 - 3、由于此类组件会公用并常用,于是在 src/layout/components 目录下创建组件【LeeInput、LeePassword、LeeTextArea、LeeSearch、LeeOTP、LeeGroup】
- 4、在 LeeInput 组件内使用
Object.assign 进行子组件挂载,对 <Input.TextArea/> 写法进行兼容 - 5、LeeInput 组件中重写
onBlur 函数,使用 setter 与 dispatchEvent 派发 input 事件同步 value,同时为了防止组件内的 handleBlur 函数影响外部 onBlur 逻辑,在函数内部执行外部 props.onBlur?(e)
目录结构
src/layout/components├── Lee-Access│ └── index.tsx # 权限控制组件,根据按钮权限决定是否渲染 children/fallback├── Lee-Button│ └── index.tsx # 带按钮权限校验的 Button 二次封装,内部基于 antd Button├── Lee-Input│ └── index.tsx # Input 统一封装入口,补充 trim 逻辑并挂载 TextArea/Search/Password/OTP/Group├── Lee-Rich-Text│ ├── index.scss # 富文本编辑器样式文件,处理全屏态和容器高度│ └── index.tsx # Quill 富文本组件,支持双向绑定、只读、占位符和全屏切换├── Lee-Upload│ ├── index.tsx # 上传组件封装,支持拖拽、类型校验、大小校验、自动/手动上传和进度回调│ └── readme.md # Lee-Upload 组件独立使用文档与示例说明├── LeeGroup│ └── index.tsx # antd Input.Group 透传封装├── LeeOTP│ └── index.tsx # antd Input.OTP 透传封装├── LeePassword│ └── index.tsx # antd Input.Password 透传封装├── LeeSearch│ └── index.tsx # antd Input.Search 透传封装├── LeeTextArea│ └── index.tsx # antd Input.TextArea 透传封装└── readme.md # 当前组件目录总览文档,说明目录结构与文件职责
核心代码
/** @format */import { Input } from "antd";import type { InputProps } from "antd/es/input";// 引入组件,进行子组件挂载,兼容antd联合组件写法import LeeTextArea from "../LeeTextArea";import LeeSearch from "../LeeSearch";import LeePassword from "../LeePassword";import LeeOTP from "../LeeOTP";import LeeGroup from "../LeeGroup";type LeeInputComponent = ((props: InputProps) => React.JSX.Element) & { TextArea: typeof LeeTextArea; Search: typeof LeeSearch; Password: typeof LeePassword; OTP: typeof LeeOTP; Group: typeof LeeGroup;};// 子组件挂载const LeeInput: LeeInputComponent = Object.assign( (props: InputProps) => { const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => { const trimmedValue = e.target.value.trim(); if (trimmedValue !== e.target.value) { // 通过原生 setter + input 事件同步受控值,确保 React 和 Form 能接收到 trim 后的结果 Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, "value" )?.set?.call(e.target, trimmedValue); e.target.dispatchEvent(new Event("input", { bubbles: true })); } props.onBlur?.(e); }; return <Input {...props} onBlur={handleBlur} />; }, { TextArea: LeeTextArea, Search: LeeSearch, Password: LeePassword, OTP: LeeOTP, Group: LeeGroup, });// 父组件导出export default LeeInput;
其他组件代码
仅展示其中一个组件代码,其余 Copy,其他组件如【Password、TextArea、Search、OTP、Group】想要如上处理,请参考上方 LeeInput 组件实现方式,自行编写/** @format */import { Input } from "antd";import type { PasswordProps } from "antd/es/input";const LeePassword = (props: PasswordProps) => { return <Input.Password {...props} />;};export default LeePassword;