react项目问题(不断更新)

一. react项目关于antd表单数据填充问题(包括hooks问题)

问题的出现

在用react函数组件时,我想像form表单中填充选中项的信息,想实现如下图,点击修改会自动填充在这里插入图片描述
核心代码如下:

1
2
3
4
5
6
7
8
9
const form = formRef.current // 使用 getFieldsValue 获取多个字段值 
if (form) {
form.setFieldsValue({
'username': curruser.username,
'mail': curruser.email,
'phone': curruser.phone,
'role': roleNames[curruser.role_id]
})
}

可是这产生大问题

问题的产生与解决

  1. 页面刷新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 // 使用 getFieldsValue 获取多个字段值
if (form) {
form.setFieldsValue({
'username': curruser.username,
'mail': curruser.email,
'phone': curruser.phone,
'role': roleNames[curruser.role_id]
})
}
  1. 又出现了一个有意思的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 // 使用 getFieldsValue 获取多个字段值
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)
}
}
  1. 产生的问题:因为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。

感兴趣的可以下载运行哦

二、如何获取或更新Form中的值

注意,代码段中username,name,age必须与form表单中form.item设置的name名相同。

  1. 更新Form表单里面的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //函数组件
    //在Form中添加属性ref={formRef}
    const formRef = React.createRef()
    formRef.current.setFieldsValue({
    username: 'name',
    })
    //class组件
    //在Form中添加属性ref={this.formRef}
    formRef = React.createRef()
    this.formRef.current.setFieldsValue({
    username: 'name',
    })
  2. 用createRef/useRef方法可以获取表单中的值

1
2
3
4
5
6
7
8
9
10
11
12
//函数组件
//在Form中添加属性ref={formRef}
const formRef = React.createRef()
const form = formRef.current // 使用 getFieldsValue 获取多个字段值
const values = form.getFieldsValue(['name','age'])
console.log(values)
//class组件
//在Form中添加属性ref={this.formRef}
formRef = React.createRef()
const form = this.formRef.current // 使用 getFieldsValue 获取多个字段值
const values = form.getFieldsValue(['name','age'])
console.log(values)
  1. 用useForm方法也可以
    1
    2
    3
    4
    5
    6
    7
    const [form] = Form.useForm();
    //在Form表单中加上form={form}
    <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
//Admin组件
//点击创建的处理
const onFinish = (values) => {
console.log('Success:', values)
//没有找到ones地址,报异常处理
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>

五、自定义Form校验规则

看了antd4官方文档,没有说清楚如何自定义其他的校验规则,比如数值大小范围,字数多少等。
可以用这个办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//自定义规则,比如这里是项目描述不能超过200字符
const checkNameLength = (value) => {
if (value.length > 200) {
return Promise.reject('不能超过200字符')
} else {
return Promise.resolve()
}
}
//在Fore.Item调用就可以生效了
<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
//hooks设置状态
const [isRed, setIsRed] = useState(false)
<Search
placeholder="请输入Ones空间地址"
allowClear
enterButton="添加"
size='middle'
onSearch={(onesAddr) => {
console.log(onesAddr)
//判断是否是合法Ones地址,非法则红色高亮,暂时假设输入111点击添加变红,其他都是黑色
if (onesAddr === '111') {
setIsRed(true)
} else {
setIsRed(false)
}
}}//添加ones地址,成功和失败的处理
className={isRed ? 'red' : ''}//在这里加类名,less文件加样式
//输入变化时,变回黑色
onChange={()=>setIsRed(false)}
/>
//less文件
.red {
.ant-input {
color: red;
}
}

改好的样子,因为还没有后台接口,暂时设置输入111点击添加变红
question
再输入其他的就会自动变回黑色
question

八、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
//子组件SearchTags
setTaskTitles([taskTitle, ...taskTitles]);
onesTag.push({ taskTitle, onesUrl });
value?.push(onesUrl);// value为urls
onChange && onChange(value);
//父组件
<Form.Item
label="Ones空间"
name="onesName"
//通过getValueFromEventtags转换为字段
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
// 先封装一个ajax请求,以便于复用
import axios from 'axios'
import {message} from 'antd'

export default function ajax(url, data={}, type='GET') {

return new Promise((resolve, reject) => {
let promise
// 1. 执行异步ajax请求
if(type==='GET') { // 发GET请求
promise = axios.get(url, { // 配置对象
params: data // 指定请求参数
})
} else { // 发POST请求
promise = axios.post(url, data)
}
// 2. 如果成功了, 调用resolve(value)
promise.then(response => {
resolve(response.data)
// 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
}).catch(error => {
// reject(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'));
上一篇

GIT常用命令