React学习笔记:Hooks及相关的各种

关注了React的作者之一Dan Abramov的博客,看了其中几篇文章(算是一个小的系列吧),非常受用。

A complete guide to useEffect

文如其名,这是一篇关于useEffect的完整使用指南。但它并不是科普式地告诉你如何使用,而且基于读者已经尝试过使用useEffect`,对它有一定的了解的基础上,对它实现的机制以及大家容易犯的错误进行了总结。

propsstate在当前渲染中不会变化

他首先澄清了React中state的本质。我们会通过setState来改变state,然后观察到页面的变化,但实际上,state并不是data bindingwatcher或是proxystate在当前render的结果里是一个常量,它在当前的render中是不变的。不仅是stateevent handlerseffects在当前的render中都是不变的。用Dan的话说,each render has its own everything。

state更新时,React重新渲染组件,每一次渲染的时候,组件只“看到”这次渲染的state值。同样地,event handler在执行的时候获取当前的值,返回的也是此次渲染的event的返回值。即使是异步函数也只获取此次渲染的state值。effect亦是如此。

但如果你想试图读取下一次渲染的state时,有一个方法是通过Ref来实现,只是这个方法无法保证每一次调用都拿到相同的值。

1
2
3
4
5
6
7
8
9
10
11
function Example() {
const [count, setCount] = useState(0)
const latestCount = useRef(count)

useEffect(() => {
latestCount.current = count;
setTimeout(() => {
console.log(`You clicked ${latestCount.current} times`)
}, 3000)
})
}

关于cleanup

有一些函数会清除掉函数副作用。比如我们会在componentWillUnmount时取消定时任务,或者新的effect写法是在useEffectreturn ()=> cleanup function

React为了实现性能的优化,不影响浏览器重新paint的过程,会在paint结束后再执行effect,因此effect中的cleanup会被延迟,它实际上是在新的一次渲染后执行的。

同步,与生命周期无关

React会根据当前的propsstate更新DOM节点,因此在渲染时,mountupdate并没有区别。

何时调用useEffect中的任务

为了避免不必要的重复渲染,可以给useEffect传一个数组参数作为依赖,只有当数组中的值发生变化时才会触发useEffect中的函数。当依赖中的值和前一次相同时,react会跳过这一次effect

当我们使用时,会情不自禁地套用class的思维模式使用effect,比如传一个空数组给useEffect来实现componentDidMount的效果,但这并不是一个好的选择,Dan把这张用法称作“欺骗React”。正确的用法是,所有effect用到的propsstatefunctions都应该作为依赖传给useEffect。这是因为传入空数组的效果是只渲染一次(即我们想要的类似componentDidMount的效果),但这样也会漏掉propsstate发生变化时本应触发的再次渲染。

Dan提到有两个使用原则,第一是所有effect用到的内容都应该放入依赖。

1
2
3
4
5
6
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => clearInterval(id)
}, [count])

第二是让effect内的函数“自给自足”,这样如果我们传入一个空数组作为依赖并不是因为欺骗,而是整个函数的确不必依赖任何值。

1
2
3
4
5
6
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1)
}, 1000)
return () => clearInterval(id)
}, [])