-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
解密React state hook #204
Comments
写得很好 其实 React 有点复杂化这里了,虽然应该是经过调研的。即,所有情况都采用立即计算(源码里的 eager compute)是最简单也最符合直觉的,否则像现在这样,多出的一次 render 是很蛋疼的,虽然用这换来了 better UI experience。我曾经问过原因,不过好像没回哈哈 |
多个更新队列那个例子里,我把useState顺序调换了下
结果是 可见setCounter2更新队列中的更新函数,并不总是先于setCounter更新队列中的更新函数优先执行。 |
我用你的例子,把两个useState换了下顺序还是这个结果啊。
这么声明的话,期望的输出应该是
实际却是
|
还有一个问题不太理解,关于懒计算切换立即计算的时机。 举个栗子,按照博文说的,分析下
第一次点击setCounter,输出如下
第二次点击setCounter,输出如下
前面为止,用博文所提内容都能解释得通,等到第三次点击setCounter,结果貌似就无法照上面来解释了。
原本以为自第二次setCounter之后都应该采用懒加载了,但这里切换为了立即计算,不太理解。 |
这个我想明白了,博主这句话应该要严谨点,加个前提条件:懒计算时。 懒计算的时候,是等到再次执行useState的时候,才会执行对于更新队列里的更新函数,这时候更新队列执行顺序就取决于useState的声明顺序。 立即计算的时候,与更新队列没有关系,是根据setState的顺序,依次执行。 拿这个栗子分析一下
点击setCounter,输出结果是
|
这是什么原因呢?同问。 |
@yaofly2012 对,初始state导致立即计算,立即计算内部导致懒计算,懒计算时遇到count2的useState触发批处理,然后遇到count1的useState |
关键词:
开始
先看个问题,下面组件中如果点击3次“setCounter”按钮,控制台输出是什么?
正确的答案:
state
的值变成2触发一次re-render
;即输出:
state
没有变,但是又触发了一次组件Counter
re-render,但是没有触发组件Display
re-render;即输出:
state
没有变,也没有触发re-render
。一、更新队列
1.1 什么是更新队列
其实每个state hook都关联一个更新队列。每次调用
setState
/dispatch
函数时,React并不会立即执行state
更新函数,而是把更新函数插入更新队列里,并告诉React需要安排一次re-render。先点击下"Add"按钮(后面解释原因),再点击“setCounter”按钮看下输出:
通过例子可以看出在执行事件处理函数过程中并没有立即执行
state
更新函数。这主要还是为了性能优化,因为可能存在多处setState
/dispatch
函数调用。1.2 多个更新队列
每个
state
都对应一个更新队列,一个组件里可能会涉及多个更新队列。useState/useReducer
的先后顺序)。点击"setCounter2"按钮看看输出结果。上例中
setCounter
对应的更新队列的更新函数永远要先于setCounter2
对应的任务队列的更新函数执行。二、懒计算
什么时候执行更新队列的更新函数呢?懒计算就是执行更新函数的策略之一,即只有需要
state
时React才会去计算最新的state
值,即得等到再次执行useState
/useReducer
时才会执行更新队列里的更新函数。先点击下"Add"按钮,再点击“setCounter”按钮看下输出:
会发现此时先执行的渲染函数,再执行更新函数。第二个更新函数的实参就是第一个更新函数的返回值。
三、批处理
在懒计算中只有再次执行渲染函数时才会知道
state
是否发生变化。那React什么时候再次执行组件渲染函数呢?一般我们都是在事件处理函数里调用
setState
,React在一个批处理里执行事件处理函数。事件处理函数执行完毕后如果触发了re-render
请求(一次或者多次),则React就触发一次且只触发一次re-render
。3.1 特性
1. 一个批处理最多触发一次
re-render
, 并且一个批处理里可以包含多个更新队列;点击"setCounter"按钮,看下输出:
2. 批处理只能处理回调函数里的同步代码,异步代码会作为新的批处理;
点击"setCounter"按钮,看下输出:
触发两次批处理。
3. 异步回调函数里触发的
re-render
不会作为批处理setTimeout/setInterval
等异步处理函数调用并不是React触发调用的,React也就无法对这些回调函数触发的re-render
进行批处理。点击setCounter按钮输出:
可以看出事件处理函数的里两次
setState
进行了批处理,而setTimeout
回调函数里的两次setState
分别触发了两次re-render。3.2 总结
useEffect
副作用函数;在实现
getDerivedStateFromProps
中会遇到这种调用场景。非React触发调用的回调函数,比如
setTimeout/setInterval
等异步处理函数四、跳过更新
我们都知道如果
state
的值没有发生变化,React是不会重新渲染组件的。但是从上面得知React只有再次执行useState
时才会计算state
的值啊。为了计算最新的
state
需要触发re-render,而state
如果不变又不渲染组件,这好像是个先有蛋还是先有鸡的问题。React是采用2个策略跳过重新渲染:4.1 立即计算
除了上面提到的都是懒计算,其实React还存在立即计算。当React执行完当前渲染后,会立马执行更新队列里的更新函数计算最新的
state
:state
值不变,则不会触发re-render
;state
值发生变化,则转到懒计算策略。当上一次计算的
state
没有发生变化或者上次是初始state
(说明React默认采用立即计算策略),则采用立即执行策略调用更新函数。1. 当前
state
是初始state;点击“setCounter”按钮看下输出:
这样说明了React默认采用立即执行策略。
2. 上一次计算
state
不变先点击两次或者更多次"setCounter2"按钮(营造上次计算结果是
state
不变),再点击一次“setCounter”按钮看下输出。4.2 懒计算
懒计算就是上面说到的那样。懒计算过程中如果发现最终计算的
state
没有发现变化,则React不选择组件的子组件,即此时虽然执行了组件渲染函数,但是不会渲染组件的子组件。点击两次“setCounter2”按钮,看下输出:
第二次点击虽然触发了父组件
re-render
,但是子组件Display
并没有re-render
。懒计算导致的问题只是会多触发一次组件
re-render
,但这一般不是问题。ReactuseState
API文档也提到了:4.3 立即计算自动转懒计算
在一个批处理中采用立即计算发现
state
发生变化,则立马转成懒计算模式,即后面的所有任务队列的所有更新函数都不执行了。点击“setCounter”按钮,看下输出:
执行完
更新函数2
时state
发生了变化,React立马转成懒加载模式,后面的更新函数都不立即执行了。4.4 重新认识跳过更新
什么是跳过更新
effect
回调。什么情况下会跳过更新
state
没有发生变化时会跳过更新;setState/dispatch
时也会触发跳过更新。点击setCounter 2按钮输出:
可以看到
state=2
触发的更新被跳过了。五、总结下
re-render
;state
不变时不重新渲染组件,又要实现懒计算state
。参考
The text was updated successfully, but these errors were encountered: