欢迎访问个人博客html
原文地址:React Hooks技术最佳实践(二)react
useEffect
是除useState
以外使用最经常使用的Hooks之一,它能够用来管理反作用,替代传统class组件中的componentDidMount
和componentWillUnmount
方法或是根据依赖项来执行代码。数组
useEffect
的用法并不复杂,可是若是对于它的执行过程和机制不熟悉的话,仍是很容易出现死循环、依赖运算错误等问题。同时,它每次运行都至关于对当前的状态存储了一份快照,这就这意味在它当中执行setTimeout
获取到的都不是最新的状态,而是运行时的状态。想要更加深刻了解的推荐阅读useEffect 完整指南。浏览器
本文章属于React Hooks技术最佳实践的第二篇文章,第一篇介绍了useState
的最佳实践,感兴趣的朋友能够阅读React Hooks技术最佳实践(一)。异步
经过设置useEffect
的第二个依赖参数为[]
能够替代componentDidMount
,虽然它们的执行时机不是彻底一致的。这样咱们就能够在useEffect
中处理须要在组件挂载后的操做或者异步请求,它可以获取到state
。由于依赖项为[]
,因此在整个组件生命周期中只会在挂载时运行。async
useEffect(() => {
// 在组件挂载后处理事务或反作用
// 可以获取到state
}, [])
复制代码
先来看看如何正确的使用async
方法。ide
错误的示范函数
useEffect(async () => {
const { data, result } = await asyncRequest();
}, [])
复制代码
useEffect
并不建议你在回调函数中直接使用async
,这样使用会直接触发报错,一般若是安装了相关lint的话会有提示。由于这么使用会致使回调函数相互之间产生竞争状态,而Effect回调函数应该是同步的。布局
推荐的作法post
const requestFn = async () => {
const { data, result } = await asyncRequest();
}
useEffect(() => {
requestFn();
}, [])
复制代码
若是须要在获取异步数据以后更新state
,也能够在requestFn
函数中处理。
const [value, updateValue] = useState();
const requestFn = async () => {
const { data, result } = await asyncRequest();
updateValue(value);
}
useEffect(() => {
requestFn();
}, [])
复制代码
若是异步请求依赖任何的state
,则须要清晰的写在useEffect
的依赖项中,这样每次在state
改变以后都会执行异步请求。
const requestFn = async (param) => {
const { data, result } = await asyncRequest(param);
updateValue(value);
}
useEffect(() => {
requestFn(param);
}, [param])
复制代码
咱们知道每次state
或者props
改变都会致使组件的re-render,因此useEffect
在没有任何依赖项时每次都会执行一遍。这时若是在它当中改变了state
,那么就会致使死循环。过程就是执行useEffect
改变了state
,而改变state
又致使了重复执行useEffect
。
错误的写法
const [count, updateCount] = useState(0);
useEffect(() => {
updateCount(prevCount => prevCount++);
})
复制代码
正确的写法一
const [count, updateCount] = useState(0);
// 增长前置条件,知足时才执行更新状态
useEffect(() => {
if (count < 1) {
updateCount(prevCount => prevCount++);
}
})
复制代码
正确的写法二
const [count, updateCount] = useState(0);
const [num, updateNum] = useState(1)
// 依赖num,每次num改变后才会执行Effect
useEffect(() => {
updateCount(prevCount => prevCount+num);
}, [num])
复制代码
除了使用state
、props
做为依赖项,函数也是能够直接做为依赖项使用。不过因为函数每次在组件渲染时都会从新执行,因此Effect会不必的重复执行。
不过可使用useCallback
方法来避免这种状况。
每次渲染都重复执行Effect
const doSomething = () => {}
useEffect(() => {
// do somethings
}, [doSomething])
复制代码
使用useCallback
const doSomething = () => useCallback(() => {}, [])
useEffect(() => {
// do somethings once
}, [doSomething])
复制代码
若是函数依赖任何其余的状态执行,则能够将依赖加入到useCallback
的依赖数组项中,这样在依赖项改变后函数都会从新执行,Effect因为依赖了函数,因此Effect也会执行。
正确的使用Effect的依赖是很是重要的,每次依赖改变后都会使用Object.is
方法来比较,若是不一样,则执行Effect。因此咱们能够经过Memoization技术来优化,具体来讲就是每次状态改变后都会与以前记忆的状态做对比,若是值发生了改变,则返回新的状态,并记忆,若是值没有改变,只是引用变了,则返回记忆的状态。这样Effect只在值改变后才执行。
这一部分在以前的文章中有介绍,这里就不具体展开了。
大多数状况下使用useEffect
便可,不过当你须要在useEffect
中操做DOM时,为了优化渲染效果,可使用useLayoutEffect
。它会在DOM更新完成以后再执行,同时能够读取到DOM布局并同步触发渲染,以后浏览器才进行绘制。
下一篇介绍其余的Hooks的用法与技巧。
官方的FAQ其实写的很不错,不少陷阱和技巧都介绍了,推荐阅读。