关注了React的作者之一Dan Abramov的博客,看了其中几篇文章(算是一个小的系列吧),非常受用。
A complete guide to useEffect
文如其名,这是一篇关于useEffect
的完整使用指南。但它并不是科普式地告诉你如何使用,而且基于读者已经尝试过使用useEffect`,对它有一定的了解的基础上,对它实现的机制以及大家容易犯的错误进行了总结。
props
和state
在当前渲染中不会变化
他首先澄清了React中state
的本质。我们会通过setState
来改变state
,然后观察到页面的变化,但实际上,state
并不是data binding
、watcher
或是proxy
。state
在当前render的结果里是一个常量,它在当前的render中是不变的。不仅是state
,event handlers
、effects
在当前的render中都是不变的。用Dan的话说,each render has its own everything。
当state
更新时,React重新渲染组件,每一次渲染的时候,组件只“看到”这次渲染的state
值。同样地,event handler
在执行的时候获取当前的值,返回的也是此次渲染的event
的返回值。即使是异步函数也只获取此次渲染的state
值。effect
亦是如此。
但如果你想试图读取下一次渲染的state
时,有一个方法是通过Ref
来实现,只是这个方法无法保证每一次调用都拿到相同的值。
1 | function Example() { |
关于cleanup
有一些函数会清除掉函数副作用。比如我们会在componentWillUnmount
时取消定时任务,或者新的effect
写法是在useEffect
中return ()=> cleanup function
。
React为了实现性能的优化,不影响浏览器重新paint
的过程,会在paint
结束后再执行effect
,因此effect
中的cleanup会被延迟,它实际上是在新的一次渲染后执行的。
同步,与生命周期无关
React会根据当前的props
和state
更新DOM节点,因此在渲染时,mount
和update
并没有区别。
何时调用useEffect
中的任务
为了避免不必要的重复渲染,可以给useEffect
传一个数组参数作为依赖,只有当数组中的值发生变化时才会触发useEffect
中的函数。当依赖中的值和前一次相同时,react会跳过这一次effect
。
当我们使用时,会情不自禁地套用class的思维模式使用effect
,比如传一个空数组给useEffect
来实现componentDidMount
的效果,但这并不是一个好的选择,Dan把这张用法称作“欺骗React”。正确的用法是,所有effect
用到的props
、state
、functions
都应该作为依赖传给useEffect
。这是因为传入空数组的效果是只渲染一次(即我们想要的类似componentDidMount
的效果),但这样也会漏掉props
、state
发生变化时本应触发的再次渲染。
Dan提到有两个使用原则,第一是所有effect
用到的内容都应该放入依赖。
1 | useEffect(() => { |
第二是让effect
内的函数“自给自足”,这样如果我们传入一个空数组作为依赖并不是因为欺骗,而是整个函数的确不必依赖任何值。
1 | useEffect(() => { |