副作用函数 (useEffect)
执行顺序 useState=>render=>useEffect
,建立真实的DOM树之后(即浏览器渲染完成页面之后)开始调用
- 什么是副作用?
react 副作用主要作用是:React组件与React之外的第三方系统数据同步,例如:发送分析日志、服务器请求或者用React状态控制非React组件等等;是由React组件的渲染结束触发的。
// 常用场景
// 一、浏览器Api
//// react组件渲染完成后向其他组件发送消息(window.postMessage)
//// video组件渲染结束后,播放视频(video.play)
// 二、请求服务器数据(网络请求)
// 三、渲染其他非React的组件(第三方)
// 使用技巧
// 一、只在React组件第一次渲染完成后执行一次
//// 将依赖传入一个空数组 useEffect(()=>{}, [])
// 二、useEffect的依赖数组触发执行
//// 当传入多个依赖项时:组件初始渲染完成会触发一次执行,同时a,b,c其中一个发生改变都会触发useEffect的一次执行 useEffect(()=>{}, [a,b,c])
// 三、每次组件渲染完成都会执行
//// 不传入依赖项 useEffect(()=>{})
// 四、useEffect中不支持异步操作
//// async/await 不支持
//// useEffect(async()=>{
//// const a = await postApi()
//// console.log(a) // 不会打印 postApi 返回的数据
//// }, [])
// 五、useEffect在再次执行前会调用上次清理函数(cleanup)
//// 注意在组件卸载时清理一些连接,如事件监听window.removeEventListener。
//// 为此,react在开发环境中,组件在挂载完成后会立即再执行一次挂载,此时,useEffect会在初始挂载执行2次,方便开发调试。如果想屏蔽此效果,可通过清理函数实现。
// 六、可用计算修改组件渲染的数据,不用useEffect处理
/* good
const [A, setA] = useState(1)
const [B, setB] = useState(0)
// good
const C = A + B // 可自动触发组件的更新,且效率高
return (
<>
...
<div>{C}</div>
</>
)
*/
/* bad
const [A, setA] = useState(1)
const [B, setB] = useState(0)
const [C, setC] = useState(0) // 没必要
// bad
useEffect(()=>{setC(A+B)}, [A,B])
....
return (
<>
...
<div>{C}</div>
</>
)
*/
// 七、如需要初始化数据,可通过在父组件中给组件添加key自动组件初始化
/*
// 父组件
return (
<>
<ChildComponent changeData={pid} key={pid} />
</>
)
// 子组件
const ChildComponent=({changeData})=>{
const [params, paramsSet] = useState('')
// 当changeData改变时,组件的params 自动更新
}
*/
// 八、当传入的数据是个数组或对象时,避免使用副作用,用直接比较触发重新渲染
//// 1. 数组数据
/*
good
const ChildComponent=({items=[]})=>{
const [A, setA] = useState(1)
const [B, setB] = useState(null)
const [prevItems, prevItemsSet] = useState(items)
// 直接判断处理数据state,state变化后自动触发组件渲染
if(prevItems !== items){
prevItemsSet(items)
setB(null)
}
}
*/
/*
bad
const ChildComponent=({items=[]})=>{
const [A, setA] = useState(1)
const [B, setB] = useState(null)
//
useEffect(()=>{
setB(null) // 会引起不必要的重新渲染流程
}, [items])
}
*/
//// 对象中的数据更新
/* good
const ChildComponent=({configs={},changeConfigs=()=>{}})=>{
// 包装一个函数,让组件中的事件触发嵌套调用
const resetSome=(changes)=>{
changeConfigs(changes)
moreSetFun(configs.line)
}
// 修改事件嵌套调用 resetSome
const handleChanges=(changes)=>{
resetSome(changes)
}
}
*/
/* bad
const ChildComponent=({configs={},changeConfigs=()=>{}})=>{
// 没必要 会引起没必要的bug, 引起页面多次渲染相同数据
useEffect(()=>{
if(configs.line){
moreSetFun(configs.line)
}
}, [configs])
// 修改事件嵌套调用 resetSome
const handleChanges=(changes)=>{
resetSome(changes)
}
}
*/
// 九、事件监听最佳选择也不是useEffect, 可用订阅hook替代
/*
function subscribe (cb){
window.addEventListener(event, cb)
return ()=>{
window.removeEventListener(event, cb)
}
}
function useSubscribe(){
// React hook 可用于订阅其他状态
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
}
const ChildComponent = ()=>{
const sub = useSubscribe()
}
*/
- 相当于类组件中的
componentDidMount
与componentDidUpdate
之后的回调