最近常用的是golang、python、最新项目虽然用了react,但直接上了低代码,这么一回想,已经1年多没有正经用react了。发现自己连form表单都倒腾了好久,好记性不如烂笔头。
关于ts,很长一段时间,我会要求自己完全禁用any,不过自从golang写习惯后意识到ts真的只是个半成品,当然可以花费时间去刻意追求,这很好,但哪怕追求到极致也用着不顺(对我来说),所以下面案例我对ts可能非常随心所欲,希望读者根据自己的情况,减少any
upload
有这么几个需求:
- 上传文件,这个官方文档里最普通案例就有,可以直接使用
- 异步初始化数据(初始化列表是通过接口异步获取的):这个需要使用使用fileList,不能使用defaultFileList
- 下载功能,如果是静态资源,可以有路径了直接下载,但如果是使用参数请求后端接口,需要额外处理,目前一期只用静态资源,这种方式不是很安全,没有做权限管控,对于敏感信息不能这样做
- 校验,官方文档有案例,对后缀名的校验需要重新修改
<Col span={24}>
<Form.Item label="附件" name="filelist">
<Upload action="/api/file/upload/requirement/create/" onChange={uploadOnChange}
listType="picture" beforeUpload={(file: any) => beforeUpload(file)}
fileList={defaultFileList}>
<button style={{ border: 0, background: 'none' }} type="button">
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传</div>
</button>
</Upload>
</Form.Item>
</Col>
富文本编辑器
安装
npm install braft-editor --save
yarn add braft-editor
使用
引入
import BraftEditor from 'braft-editor'; // 引入编辑器组件
import 'braft-editor/dist/index.css'; // 引入编辑器样式
react组件中使用如下代码,设置必要的值
const [editorState, setEditorState] = useState(BraftEditor.createEditorState('')); // 设置编辑器的初始内容
const [isEdit, setIsEdit] = useState(false)
组件的渲染,我渲染在了form表单里
<Form disabled={!isEdit} form={form}>
<Col span={24}>
<Form.Item label="详情" name="remark" rules={rules.remark}>
<BraftEditor onChange={handleEditorChange}
className="draft-editor-content-wrapper" readOnly={!isEdit}/>
</Form.Item>
</Col>
</Form>
异步数据初始化,使用能查到的最常见方法无法初始化,可以参考下方issues
https://github.com/margox/braft-editor/issues/341
const [form] = Form.useForm()
form.setFieldsValue({
remark: BraftEditor.createEditorState(baseInfo?.remark || ''),
})
修改数据
const handleEditorChange = (e: any) => {
// 更新编辑器的状态, 不论是否为空状态,都把内容填入,等最后上传的时候确认内容(因为用户也可能把内容改成空的)
// 是否为空使用自定义校验规则
form.setFieldValue('remark', e.toHTML())
};
规则校验
const rules = {
remark: [
{ required: true, message: '请输入详情' },
{
validator: (_: any, value: string) => {
if (value === '<p></p>') {
return Promise.reject('详情不能为空');
}
return Promise.resolve();
}
}
]
}
如何知道是否手机端
import { Steps,Button,Grid } from 'antd';
const { useBreakpoint } = Grid;
const screens = useBreakpoint();
通用操作:自定义组件、数据缓存、防抖
自定义组件
form表单中我们可能会想要使用一些自定义组件,特别是公共组件,此时直接丢进form表单将无法正常使用form的校验,无法取得正常值;
父组件引入子组件:正常引入,正常使用
import UserPart from '../../components/base/user/index';
<Form form={form} labelCol={{
xs: { span: 6 },
sm: { span: 3 },
// style: {textAlign: screens.xs? 'left':'right' }
}}>
<Form.Item label="负责人" name="manager" rules={rules.manager}>
<UserPart />
</Form.Item>
</Form>
不管是form表单的校验还是值操作,保持正常
let checkRes = await form.validateFields()
form.getFieldsValue()
子组件关键是onChange的绑定或者自定义操作
import React, { useEffect } from "react";
import { Select } from "antd";
import {getUser} from "../service";
const UserPart: React.FC<{value?:any; onChange?:any}> = (props) => {
const {value, onChange} = props;
const [userList, setUserList] = React.useState([]);
useEffect(() => {
getUser().then((res) => {
setUserList(res.data||[]);
console.log(value)
}).catch (err => {
console.log(err)
})
}, []);
return <Select showSearch value={value}
filterOption={(input, option) =>
((option?.label + '__' + option?.pinyin) ?? '').toLowerCase().includes(input.toLowerCase())
}
placeholder="请选择"
options={userList.map((item: any) => {
return {
label: `${item.fullName}(${item.username}) - ${item.department||""}`,
value: item.username, pinyin: item.pinyin }
})}
onChange={onChange}
allowClear></Select>
}
export default UserPart;
数据缓存
公共组件,比如下面案例里的用户列表,并不需要每次都获取最新数据,直接缓存可以减少数据库压力,但是同时也没必要用各种三方工具或者storage来存储,直接使用语言本身的特性,内存存储即可
import {axios} from '../../plugin';
// 缓存用户数据
let cacheUserInfo: any = null;
// getUser获取用户
export async function getUser() {
if (cacheUserInfo) {
return Promise.resolve(cacheUserInfo);
}
try {
let res = await axios.get(`/api/base/user/`);
cacheUserInfo = res;
return Promise.resolve(cacheUserInfo);
} catch (err) {
return Promise.reject(err);
}
}
防抖
用拍照类比,当你拍照手抖时,仍然只会拍1清晰的照片,因为你会控制自己等手抖结束,聚焦清晰后按下快门。函数防抖就是,当你执行,但触发了很多次(特别是用户很急的时候,会疯狂快速多次点击按钮),程序控制当多次点击触发时,只提交最后一次。
防抖函数不是很复杂的原理,只要内部有个计时器,看上次点击和这次点击的时间差,不过有现成的工具,就用了现成的工具
import { debounce } from 'lodash';
// 防抖,防止多次提交
const handleOk = debounce( async () => {
console.log("我快速提交")
console.log(form.getFieldsValue())
setLoading(true)
// 数据的校验
try {
let checkRes = await form.validateFields()
} catch (errorInfo) {
setLoading(false)
}
// 提交
setLoading(false)
}, 1000);
其他
当然还会有很多其他的细节,诸如对于规则的书写,加载中状态的展现,这个案例是一个弹框
所以状态的展现操作的是Modal的属性
<Modal title="新增/编辑" open={isOpen} onOk={handleOk} onCancel={handleCancel} width="1000px"
confirmLoading={loading} okText="确认" cancelText="取消">
校验规则的书写
const rules = {
nameValue: [
{ required: true, message: '请输入标题' },
{ max: 50, message: '标题不能超过50个字符' },
{ min: 5, message: '标题不能少于5个字符' }
],
manager: [
{ required: true, message: '请选择负责人' }
],
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。