一. react项目关于antd表单数据填充问题(包括hooks问题)
问题的出现
在用react函数组件时,我想像form表单中填充选中项的信息,想实现如下图,点击修改会自动填充
核心代码如下:
1 2 3 4 5 6 7 8 9
| const form = formRef.current if (form) { form.setFieldsValue({ 'username': curruser.username, 'mail': curruser.email, 'phone': curruser.phone, 'role': roleNames[curruser.role_id] }) }
|
可是这产生大问题
问题的产生与解决
- 页面刷新curruser的数据就没有了,页面直接报错,想了下,找到了解决办法,把curruser当做一个状态用hooks保存起来,代码变成如下的样子:
1 2 3 4 5 6 7 8 9 10 11 12
| setcurruser(user) setisModalVisible(true) setCurrentId(user._id) const form = formRef.current if (form) { form.setFieldsValue({ 'username': curruser.username, 'mail': curruser.email, 'phone': curruser.phone, 'role': roleNames[curruser.role_id] }) }
|
- 又出现了一个有意思的bug,第一次点击修改,表单数据是空的,第二次点击修改,出现的是第一次点击的数据,第三次点击出现的是第二次的数据,查了资料发现是因为useState是异步的,也是哭了,继续想解决办法,可以把设置表单数据的代码放到useEffect里面或许可以解决,但是此时又出现了问题。此时代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| useEffect(() => { getUsers() const form = formRef.current if (form) { form.setFieldsValue({ 'username': curruser.username, 'mail': curruser.email, 'phone': curruser.phone, 'role': roleNames[curruser.role_id] }) } }, [users,curruser])
const getUsers = async () => { const result = await reqUsers() if (result.status === 0) { console.log('getuser') const { users, roles } = result.data setUsers(users) setRoles(roles) const roleNames = roles.reduce((pre, role) => { pre[role._id] = role.name return pre }, {}) setRoleNames(roleNames) } }
|
- 产生的问题:因为useEffect的依赖项里面有users,而users又是通过像后台请求数据获得,球球之后返回的users相当于发生了变化,导致运行useEffect里的代码,然后又调用getUsers,如此反复循环,最后产生的问题是,我无法更新表单里的数据,因为useEffect正在已很高的频率重复执行,相当于表单里的数据不断地重置。又要思考如何解决,办法就是将依赖项里面的users去掉,果然成功解决该问题,可是又出现了新的问题—无法添加用户user,因为依赖项users没有了,出现问题就要解决啊,这个简单,点击OK重新调用getUsers就好了。
总结
hooks在使用中感觉还是有很多问题的,一个是useState是异步的,想要在useState后直接使用状态要想解决办法才行,再就是useEffect,依赖项要想好再添加,这次改bug收获还是蛮大的。
个人github仓库地址是:https://github.com/2513483494?tab=repositories
这个后台管理项目也基本上算是写完了,有点丑,功能也不是很多,但是作为新手的我还是有很多收获,入门的同学可以看看。
对了,这个的仓库名是react-admin,在prac这个分支上,后端仓库名是react-admin-server,需要有node环境和mongodb。
感兴趣的可以下载运行哦
注意,代码段中username,name,age必须与form表单中form.item设置的name名相同。
更新Form表单里面的值
1 2 3 4 5 6 7 8 9 10 11 12
|
const formRef = React.createRef() formRef.current.setFieldsValue({ username: 'name', })
formRef = React.createRef() this.formRef.current.setFieldsValue({ username: 'name', })
|
用createRef/useRef方法可以获取表单中的值
1 2 3 4 5 6 7 8 9 10 11 12
|
const formRef = React.createRef() const form = formRef.current const values = form.getFieldsValue(['name','age']) console.log(values)
formRef = React.createRef() const form = this.formRef.current const values = form.getFieldsValue(['name','age']) console.log(values)
|
- 用useForm方法也可以
1 2 3 4 5 6 7
| const [form] = Form.useForm();
<Form form={form} > //用下面的方法也是可以获得form表单中的值得 const values: valueType = form.getFieldsValue(['projectName', 'projectDesc']);
|
三、在Modal组件中的p标签不能自动换行
解决办法:
1
| <p style={{ wordWrap: 'break-word' }}>
|
四、子组件正常路由跳转,父组件跳转却失效
问题代码:
1 2 3 4
| import Admin from '../home/Home' <Modal visible={modalVisible} footer={null} onCancel={handleCancel}> <Admin /> </Modal>
|
1 2 3 4 5 6 7 8 9 10 11 12
|
const onFinish = (values) => { console.log('Success:', values) if (values.onesName !== '1') { message.error('没有找到您所输入的Ones空间,请检查地址是否正确') } else { props.history.push('/role') } }
|
解决办法:问题就出在父子组件上,在父组件调用子组件定义的跳转事件时,要传递history
第一个代码改成这样就好了
1 2 3 4
| import Admin from '../home/Home' <Modal visible={modalVisible} footer={null} onCancel={handleCancel}> <Admin history={props.history}/> </Modal>
|
看了antd4官方文档,没有说清楚如何自定义其他的校验规则,比如数值大小范围,字数多少等。
可以用这个办法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const checkNameLength = (value) => { if (value.length > 200) { return Promise.reject('不能超过200字符') } else { return Promise.resolve() } }
<Form.Item label="项目描述" name="projectDesc" rules={[ { validator: checkNameLength } ]} > <TextArea placeholder="请输入" autoSize={{ minRows: 2, maxRows: 6 }} /> </Form.Item>
|
六、Modal增加或取消下面的按钮
在用Modal组件的时候,经常需要增加按钮或者取消几个按钮或者压根不用按钮,怎么做呢?
在footer属性中自定义就好了,像这样,footer={null}就是底部什么都没有了:
1 2 3 4 5 6 7 8 9 10 11 12
| <Modal visible={modalVisible} footer={null} onCancel={handleCancel}> <Admin history={props.history}/> </Modal> footer={[ <Button key="back" onClick={this.handleCancel}></Button>, <Button key="submit" type="primary" loading={loading} onClick={this.handleOk}> Submit </Button>, ]}
|
七、js项目修改antd默认样式
博主遇到的需求是,Form中的Input.Search输入的字符串不合法就红色高亮。
解决方案:在谷歌浏览器查找到元素类名,比如我这里是ant-input,然后改代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const [isRed, setIsRed] = useState(false) <Search placeholder="请输入Ones空间地址" allowClear enterButton="添加" size='middle' onSearch={(onesAddr) => { console.log(onesAddr) if (onesAddr === '111') { setIsRed(true) } else { setIsRed(false) } }} className={isRed ? 'red' : ''} onChange={()=>setIsRed(false)} />
.red { .ant-input { color: red; } }
|
改好的样子,因为还没有后台接口,暂时设置输入111点击添加变红

再输入其他的就会自动变回黑色

八、ts项目修改antd默认样式
1 2 3 4 5
| .red { :global(.ant-input) { color: #FF3A30; } }
|
经过测试,发现这样才会生效,需要注意。
九、发现了新大陆—ahook,要好好用
待更新
十、受控组件传值
背景:Form.Item中有一个自定义组件,该Item需要添加自定义规则—自定义组件中的tag数必须大于0,
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| setTaskTitles([taskTitle, ...taskTitles]); onesTag.push({ taskTitle, onesUrl }); value?.push(onesUrl); onChange && onChange(value);
<Form.Item label="Ones空间" name="onesName" //通过getValueFromEvent把tags转换为字段 getValueFromEvent={(urls) => { setUrls(urls); }} //必须有tag才可以创建,tag通过ones地址添加 rules={[{ validator: () => { if (urls.length) { return Promise.resolve(); } else { return Promise.reject(new Error('请添加onesName')); } }, }]} > {/* 注意可能传递的projectId */} <SearchTags /> </Form.Item>
|
通过受控组件的value、onChange,改变value的值,让父组件通过getValueFromEvent接收到数据,这样就可以实现功能了。
也可以通过setFieldsValue,传给自定义组件固有属性value,在自定义组件内部用props.value接收
十一、请求接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import axios from 'axios' import {message} from 'antd'
export default function ajax(url, data={}, type='GET') {
return new Promise((resolve, reject) => { let promise if(type==='GET') { promise = axios.get(url, { params: data }) } else { promise = axios.post(url, data) } promise.then(response => { resolve(response.data) }).catch(error => { message.error('请求出错了: ' + error.message) }) }) }
export const reqLogin = (username, password) => ajax('/login', {username, password}, 'POST')
const result = await reqLogin(username, password)
|
之后就可以使用了,注意要根据开发文档来写哦。
也可以复杂一点,添加拦截器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| axios.interceptors.request.use(function (config) { return config; }, function (error) { return Promise.reject(error); });
axios.interceptors.response.use(function (response) { return response; }, function (error) { return Promise.reject(error); });
export const reqIsValid = (onesUrl: string) => request({ url: '/api/v1/project/searchOnesTask', method: 'get', data: { onesUrl, }, }).then((res: any) => _get(res, 'taskTitle'));
|