首次尝试使用react,用react+ts+antdesign做了个项目,初学者的坑都踩了遍,将项目过程中的踩坑记录了下来,不得不感叹react hook真的好用诶。貌似多写点介绍才能显示在缩略图中哦,强迫症迫使我啰嗦加字滴答滴答
antdesign 嵌套子表格获取异步数据
1.初步方案:在 expandedRowRender 中请求
// 子表单部分
const expandedRowRender = (record: detailParams) => {
let {errorMsg, errorOther, errorType} = record
let data = {
projectName,
errorMsg,
errorOther,
errorType
}
getErrorListDetail(data).then((da: any) => {
if (da.bizSuccess) {
da.dataList.forEach((v: any, index: number) => {
v.key = index
})
setSubDataSource(da.dataList)
} else {
setError(da.msg || '获取数据失败')
}
}).catch(err => {
setError(err)
})
const columns = [
{
title: '错误类型',
dataIndex: 'errorType',
key: 'errorType',
className: 'column-center',
width: 200
}
]
return <div>
{error ? (<Alert message={error} type="success" closable afterClose={() => setError(null)} />) : null}
<Table columns={columns} dataSource={subDataSource} pagination={false} />
</div>
};
存在的问题
点击一次加载子表格后,会不断发起请求,造成死循环
原因
expandedRowRender 是在 Table 组件的 render 方法中调用的,React render 中用调用异步请求 getErrorListDetail 会造成重复调用的问题,getErrorListDetail -> setState -> render -> getErrorListDetail -> setState -> render,循环往复
2.优化方案
将请求写在
onExpend中,渲染写在expandedRowRender中
const onExpand = (expanded: boolean, record: detailParams) => {
if (!expanded) {
// 如果不断的添加键值对,会造成数据过于庞大,浪费资源,
// 在每次合并的时候讲相应键值下的数据清空
} else {
let {errorMsg, errorOther, errorType} = record
let data = {
projectName,
errorMsg,
errorOther,
errorType
}
getErrorListDetail(data).then((da: any) => {
if (da.bizSuccess) {
da.dataList.forEach((v: any, index: number) => {
v.key = index
})
setSubDataSource(da.dataList)
} else {
setError(da.msg || '获取数据失败')
}
}).catch(err => {
setError(err)
})
}
}
存在的问题
每次点击展开,请求当前行的数据,会修改其他行的嵌套子表单数据
原因
3.最终方案
给每行一个对应的key:
rowKey={record => record.key}
const onExpand = (expanded: boolean, record: detailParams) => {
if (!expanded) {
// 如果不断的添加键值对,会造成数据过于庞大,浪费资源,
// 在每次合并的时候讲相应键值下的数据清空
const data = {
...subDataSourceObj,
[record.key]: []
}
setSubDataSourceObj(data)
} else {
let {errorMsg, errorOther, errorType} = record
let data = {
projectName,
errorMsg,
errorOther,
errorType
}
getErrorListDetail(data).then((da: any) => {
const {bizSuccess, dataList, msg} = da
if (bizSuccess) {
dataList.forEach((v: any, index: number) => {
v.key = index
})
const data = {
...subDataSourceObj,
[record.key]: dataList
}
setSubDataSourceObj(data)
} else {
setError(msg || '获取数据失败')
}
}).catch(err => {
setError(err)
})
}
}
子表展示时以
subDataSourceObj[record.key]取值
antdesign日期组件,默认为英文国际版
根据项目需求,需要中文版
import moment from 'moment'
import 'moment/locale/zh-cn'
// 要放在所有引入的最后面
moment.locale('zh-cn')
<RangePicker
// 设置为中文
locale={zhCN}
showTime={{ format: 'HH:mm' }}
format="YYYY-MM-DD HH:mm"
onChange={(e, value) => {onTimeChange(value)}}
/>
此时安装moment需要注意,用npm安装的moment和antd,只有年会变成中文,因此需要用yarn add安装这两个依赖
命令yarn add antd moment,问题解决
关于中英文
- 默认antdesign也是英文,需要在index.tsx文件中加上中文转换
import cn from 'antd/es/locale/zh_CN';
ReactDOM.render(
<ConfigProvider locale={cn}>
<App />
</ConfigProvider>,
document.getElementById('root')
);
项目打包后的路径问题
1.打包后的静态资源加载不到,都是404
- 解决方案
- 在
package.json中增加一行代码:"homepage": "."
- 在
2.在生产环境刷新页面报404
- 原因
- 使用BroswerRouter在生产环境刷新会有问题
- 解决方案
- 改用hashHistory
虽然browserHistory实现的功能更多, browserHistory 使用的是 HTML5 的 History API,浏览器提供相应的接口来修改浏览器的历史记录;而 hashHistory 是通过改变地址后面的 hash 来改变浏览器的历史记录;History API 提供了 pushState() 和 replaceState() 方法来增加或替换历史记录。而 hash 没有相应的方法,所以并没有替换历史记录的功能。但 react-router 通过 polyfill 实现了此功能,好像是使用 sessionStorage。 另一个原因是 hash 部分并不会被浏览器发送到服务端,也就是说不管是请求 #foo 还是 #bar ,服务只知道请求了 index.html 并不知道 hash 部分的细节。而 History API 需要服务端支持,这样服务端能获取请求细节。 还有一个原因是因为有些应该会忽略 URL 中的 hash 部分,记得之前将 URL 使用微信分享时会丢失 hash 部分。
但在不需要太多功能的情况下,我们完全可以使用hashHistory
react hook 基础知识笔记
状态提升
- 在react中,任何可变数据理应只有一个单一数据源,在应用中要保持自上而下的数据流
- 多个组件共享同一数据,需要把这个数据提升到最近的父组件中进行管理
- 与双向数据流的区别
- 不是尝试在不同组件中同步状态,而是在数据源处对数据做综合处理
react hook
effect hook
- 无需清除的副作用
// useEffect参数是纯函数时,每次渲染(render)后都会执行
useEffect(() => {})
- 需要清除的副作用
// 返回函数里,可以清除 // click事件的清除 useEffect(() => { document.addEventListener('click', console.log(1)) return () => { document.removeEventListener('click') } })
控制effect的执行(第二个参数)
// useEffect第二个参数是空数组时,代表只执行一次
useEffect(() => {}, [])
// useEffect第二个参数数组里是变量值时,代表只在该变量变化时执行
useEffect(() => {
console.log(like)
}, [like])
HOC对比自定义hook的缺陷
HOC:higher order component 高阶组件
参数是组件,返回值也是组件
自定义hook
多次被调用的(请求等)可以封装到函数中,单独提取出来
useRef
- 修改ref的值不会引发组件的render
const likeRef = useRef(0)
console.log(likeRef.current)
- 常用用法———获取DOM节点
useContext
在祖先组件和后代组件之间,兄弟组件之间,共享值的方法(避免组件树一层层传递)
hook规则
- 只在最顶层使用Hook,不在循环或者嵌套函数中调用
- 只在react函数或自定义hook中调用hook,不能在js函数中调用hook
其他Hook
[文档连接]usehooks.com/
- useReducer 用于组件传值
- useCallback 用于性能调优