关注了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(() => { |