使用hooks踩过的坑
一、什么时候重新渲染页面?
状态是基础数据类型
用过react的同学应该都知道,状态改变了就会重新渲染啊,好,看下面的情况
1 | |
此时页面会重新渲染吗?答案是不会
但是update状态会改变吗?答案是会
再看一个例子
1 | |
点击按钮后页面会重新渲染吗?答案是不会
但是update状态会改变吗?答案是不会
综上,结论是只有用setState改变状态才会触发重新渲染。
状态是数组对象
1 | |
假设我们的代码是这个逻辑,当我点击按钮页面是否会重新渲染呢?
答案是不会,你可能想说,name不是变成tom了吗,不会是因为没用setData改变状态
那看这个例子
1 | |
这次满足了条件,用setState的方法改变了状态,点击按钮会不会重新渲染呢?
答案是也不会,因为hooks不会检查引用类型内部数据是否发生变化,只要数组地址没有变,hooks就认为状态没变
二、useEffect注意点
什么时候执行?第一次渲染之后,和每次状态更新之后
1 | |
看下输出
先渲染页面,然后执行useEffect里面的代码。
那代码编程这样之后输出什么呢
1 | |
看下输出
分析:
- 先渲染页面,所以会先执行console.log(‘ok:’, a);页面输出a的值0
- 执行useEffect里面的内容,根据事件循环机制会先执行console.log(‘useEffect’, a);页面输出a的值0
- 然后是setTimeout,等待三秒,输出a的值0
- seta(a + 1)状态更新,页面重新渲染,又执行了console.log(‘ok:’, a);输出1
- return的内容并没有执行,稍后了解为什么
改一下代码,把依赖项去掉应该可以发现这会无限循环,因为useState用在了useEffect中1
2
3
4
5
6
7
8
9
10
11
12
13const [a, seta] = useState(0)
useEffect(() => {
const t = setTimeout(() => {
console.log('setTimeout:', a);
seta(a + 1)
}, 3000)
console.log('useEffect', a);
return () => {
console.log('clearTimeout', a)
clearTimeout(t)
};
})
console.log('ok:', a);
看一下输出
在分析一下: - 先渲染页面,所以会先执行console.log(‘ok:’, a);页面输出a的值0
- 执行useEffect里面的内容,根据事件循环机制会先执行console.log(‘useEffect’, a);页面输出a的值0
- 然后是setTimeout,等待三秒,输出a的值0
- seta(a + 1)状态更新,页面重新渲染,又执行了console.log(‘ok:’, a);输出1,前面四个都与之前一样
- 状态更新页面渲染之后会创建一个新的useEffect,注意此时在调用useEffect之前会对上一个useEffect进行清理,此时,return的函数才会执行,输出被清除的useEffect中a的值也就是0
- 新的进入useEffect,无限循环2-6
之前并未对useEffect中的清除函数进行深入了解,这次总算了解了它的执行顺序。
了解了执行顺序,再看一下useEffect的精读,读后受益匪浅,总结一下:
每次render自己的props和state都是固定不变的,可以想象每次render都生成了一个新副本,里面都是固定的常量,包括事件处理和useEffect也是固定的,比如下面这个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const App = () => {
const [temp, setTemp] = React.useState(5);
const log = () => {
setTimeout(() => {
console.log("3 秒前 temp = 5,现在 temp =", temp);
}, 3000);
};
return (
<div
onClick={() => {
log();
setTemp(3);
// 3 秒前 temp = 5,现在 temp = 5
}}
>
xyz
</div>
);
};
这段代码的输出是3 秒前 temp = 5,现在 temp = 5,因为setTemp(3);执行这句话的时候又新生成一次render了,而log函数还在temp=5的那个render中,所以输出是5,这里要好好理解一下。
那怎样可以改变这个机制呢,用useRef就可以变成唯一引用,而不是每次render之间都存在隔离。
hook规则:
- 只在最顶层使用hook,不要在循环,条件或嵌套函数中调用 Hook
- 只在react中使用