hooks
useEffect()
在组件内调用,可以访问state
的变量或者props
, 执行时机是在DOM更新之后调用他
useEffect()
接受一个函数作为参数
useEffect
的执行是异步的 不会阻塞代码
如何控制
useEffect(
() => { alert('someThing') // 这是每次更新DOM后执行的代码
return () => alert('clear up') // 这是在执行前对上一个effct清除的代码
}, [type]) // 第二个参数是当这个state没有变化时,不执行
注意
当return 一个变量时, 在本回合( 最后 ) 执行代码
当return 一个函数时, 在下一次刷新之前执行
useEffect 和 async/await 不能嵌套使用
useEffect( async()=> { // 错误
订阅某状态
return 卸载订阅
}, [] )
useEffect接收的第一个参数的返回值是在组件卸载时 调用的, 假如async, 则返回一个promise对象, 执行它当然会报错, 正确用法
const fn = async () => {
fetch api...
}
useEffect(fn, [] )
子组件调用父组件方法
// 父组件
<Child handle={handle}>
// 子组件
const handle = (props) => {
props.handle()
}
父组件调用子组件方法
// 子组件
import React, { forwardRef } from 'react'
const Child = () => {
const son = () => {
console.log('我是子组件方法')
}
return (<button onClick={son} />)
}
export default forwardRef(Child)
//父组件
import React, { useState, useRef, useEffect } from "react";
// 使用useRef钩子
const parentRef = useRef()
// 创建变量接收
const [childrenEvent, setchildrenEvent] = useState()
// effect 监听parentRef
useEffect(()=>{
setchildrenEvent(parentRef)
}, [parentRef])
return (
<Child ref={parentRef} />
)
react循环遍历数组用map函数
-
map函数返回一个新的数组,在map的回调函数里,迭代每一项的时候也必须有返回值。
-
forEach 没有返回值
在需要返回值的情况下 一定要用map!!!
在需要返回值的情况下 一定要用map!!!
在需要返回值的情况下 一定要用map!!!
react ANTD 中 Form 组件中自定义组件的取值问题
对于form
组件的Form.Item
包裹其他自定义组件时, submit 会导致获取不到自定义组件的值
这时可以调用 setFieldsValue
方法, 将值赋给 表单
父组件
// 定义回调函数
const onPositionSelect = (value) => {
// console.log(value)
spaceListForm.setFieldsValue({
parentId: value[value.length - 1]
});
};
<Form.Item
label="选择上级空间点位"
name="parentId"
rules={[{ required: true, message: "请选择" }]}
>
<SelectPosition
onPositionSelect={onPositionSelect}
options={spaceData}
placeholder='请选择上级节点' />
</Form.Item>
子组件
// 接收父组件方法
const onPositionSelect = props.onPositionSelect
return (
<Cascader
options={options}
fieldNames={{ label: 'node_name', value: 'id', children: 'Children' }}
changeOnSelect
// 在onchange 的时候调用该方法
onChange={onPositionSelect}
placeholder={placeholder}
/>
);
ANTD 中表单动态增减
-
Form
嵌套Form.List
嵌套Form.Item
-
Form.Item
必须有name={[name, 'type']}
和fieldKey={[fieldKey, 'type']}
属性 , 不然会控件重复, 选择一个其他都变<Form.Item name={[name, 'type']} label="设备类别" fieldKey={[fieldKey, 'type']} > <Select onChange={typeChange} allowClear> <Option value="1">电力</Option> <Option value="2">暖通</Option> <Option value="3">自动化</Option> </Select> </Form.Item>
React ANTD 中 表格 操作栏获取数据
const columns = [
....
{
title: "操作栏",
render: (text) => <Button size="middle" onClick={()=>{cancelTask(text)}}>取消任务</Button>,
// text 是数据 οnclick={cancel()} 这样写会直接调用
// 只有用箭头函数 οnclick={()=>{cancel()}} 表示调用cancel函数
}
]
return (
<Table columns={columns} dataSource={list}>
)
ANTD中 表格 报错 唯一key值
<Table
columns={columns}
dataSource={data}
rowKey={(columns) => columns.id}
/>
加上rouKey字段, 接收一个函数, 默认参数是行数据, 指定唯一key值即可
React ANTD 中踩坑 抽屉报错 React does not recognize the switchScrollingEffect
prop on a DOM element
从官网上粘下来的代码报错 (给DOM节点绑定了未知的属性)
原因: Drawer 挂载的 HTML 节点默认为body \
解决
<Drawer getContainer={false} > // 让其挂载在当前dom上
ANTD 中 Tree 树形控件, 关于默认展开项defaultExpandAll
不生效
因为tree
组件是单向数据流, 也就是改变值不能生效(只在开始设置时有效), 而数据是异步请求,开始不存在
使用条件渲染
ANTD中抽屉组件 , 报错 React does not recognize the switchScrollingEffect
prop on a DOM element ‘‘父组件有switch属性挂载到子组件DOM上’’
因为Drawer是默认挂载在body上, 设置 getContainer = {false}
让Drawer挂载在当前dom上即可解决问题
React异步加载
React.lazy
import React, { lazy, Suspense } from 'react';
const MyEcharts = lazy(() => import("../pages/Demo/compontent/MyEcharts"));
如果需要在异步时加一些动画 需要用到Suspense
组件
<React.Suspense fallback={<waitingComponent>}>
<Component />
</React.Suspense>
React 动态路由
- 在
routes
中定义路径
const VerData = lazy(() => import("../../components/verData/verData/verData.jsx"));
const VerDataInfo = lazy(() =>
import("../../components/verData/verDataInfo/verDataInfo.jsx")
);
export const routes = [
{
path:'/verData',
component:VerData
},
{
path:'/verDataInfo/:id',
component:VerDataInfo
}
]
- 导入路由历史对象
import { useHistory} from 'react-router-dom';
// 获取路由对象
let history = useHistory();
- 使用
history.push
跳转 传参使用query
history.push({
pathname: `equipmentInfo/${item.id}`,
query: data,
});
组件接收
this.props.location.query
React useEffect 偶尔出现警告 To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function
- 问题原因 : 因为在进入组件时触发effect发请求, 请求没有完成就进入了其他组件发请求 , 导致重复请求
react 组件更新机制
Axios中 请求头设置 axios.defaults.headers.post[‘Content-Type’] 无效问题
描述 :给post请求设置content-type 为 form-data 请求中 还是json格式
解决: 使用qs插件给请求参数序列化一下
方法一
在transformRequest里 转化
axios.defaults.transformRequest = (data) => qs.stringify(data);
方法二
在拦截器里转化
axios.interceptors.request.use(
(config) => {
config.data = qs.stringify(config.data) // 转为formdata数据格式
return config;
},
(error) => {
return Promise.reject(error);
}
);
react useEffect 忽略依赖项警告
// eslint-disable-next-line react-hooks/exhaustive-deps
渲染优化
memo
<Father>
<Button onClick={()=>{xxxx}}/>
<Child />
</Father>
Qs : 当父组件点击事件触发,更新后 子组件也跟着更新
Rs: 使用memo
方法包裹子组件
import {memo} form react
const MemoChild = memo(Child)
const Aaa = () => {
const [count, setCount] = useState(1);
return (
<div>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>+1</button>
<MemoChild />
</div>
)
}
useMemo
Qs:一个组件内 某个返回值因state变化组件更新 导致多次触发
Rs:使用useMemo
包裹函数
不想谁渲染就包裹谁
const Aaa = () => {
console.log('组件更新了')
const [count, setCount] = useState(1);
const [val, setValue] = useState('');
const expensive = useMemo(() => {
console.log('compute');
let sum = 0;
for (let i = 0; i < count * 100; i++) {
sum += i;
}
return sum;
},[count])
return <div>
<h4>{count}-{expensive}</h4>
{val}
<div>
<button onClick={() => setCount(count + 1)}>+c1</button>
<input value={val} onChange={event => setValue(event.target.value)}/>
</div>
</div>;
}
// 这里的expensive函数依赖count
// 但是input输入时 改变Setval 导致组件更新
useCallback
Qs: 在向子组件传递函数props时,每次 render 都会创建新函数,导致子组件不必要的渲染
使用useCallback
无论 render 多少次,我们的函数都是同一个函数,减小不断创建的开销。
const MemoTestChild = memo(TestChild);
function Test() {
const [count, setCount] = useState(100);
const [name, setName] = useState('TestChild组件');
return (
<>
<h2>{count}</h2>
<button onClick={() => setCount(count + 1)}>++</button>
{/* <MemoTestChild name={name} setName={(newName) => setName(newName)} /> */}
// 重要的这一行代码, 确保只有当子组件调用setName时才更新子组件
<MemoTestChild name={name} setName={useCallback((newName) => setName(newName),[])} />
</>
)
}
//子组件部分
function TestChild ( {name, setName} ) {
console.log('子组件更新');
return (
<>
<h3>子组件:{name}</h3>
<button onClick={() => setName(name + '改变后的name')}>改变name</button>
</>
);
}
路由
在/home
下建立新的子路由 '/home/app'
,使用跳转history.push( pathname: "/home/app")
有时会出现问题, 根据路由模糊匹配, /home也会被匹配到 , 如果只想显示/home/app
组件需要使用精准匹配, 这里使用一种不用精准匹配的办法, 将'/home/app'
组件注册在'/home'
上面, 这样路由匹配时会先匹配到'/home/app'
注意这里不是父子级, 是平级关系
页面跳转
方法一: 引入LINK组件, 用 <Link to={’/log’}> 来跳转, 参数可以拼接在地址栏后面, 拿参数可以使用动态路由匹配后的地址栏中取参。
//入参
<Link to={`collectorDetail/${record.Id}`} className="margin-l2">设备管理</Link>
//取参
const { configId, roomId } = props.match.params;
方法二: 使用props.history.push 传参,和方法一不同的是,参数不会拼接在地址栏后, 需要注意的是,组件的props.hitstory是不是undefined, 如果是,引入withRouter组件包裹当前组件即可。
import { withRouter } from 'react-router-dom'
export default withRouter(XXX);
// 入参
props.history.push({
pathname: `/log`,
query: 33333,
})
// 取参
const params = props.location.query
样式
在修改了antd组件样式后, 使用侧边栏切换到其他页面, 因为已经载入了上一个样式文件,导致该组件所有样式都变化, 刷新之后正常, 因为载入的文件被刷新掉了