Hello everyone, I'm Casson.
React18
has entered the RC
(release candidate) stage, and is only one step away from the official version.
v18
added a lot of features. Today, we will not talk about new features, but will talk about a detail that is better than the old version of v18
:
In v18, the number of component renders may be less
Welcome to join human high-quality front-end framework group , with flying
where does the state come from
in the following components:
function App() {
const [num, update] = useState(0);
// ...省略
}
App
component render
will execute useState
and return the latest value of num
.
That is, the component must be render
to know the latest state. Why is this so?
Consider the following code that triggers an update:
const [num, update] = useState(0);
const onClick = () => {
update(100);
update(num => num + 1);
update(num => num * 3);
}
After the execution of onClick
, the update is triggered, and the update causes the App
component render
, and then useState
to execute.
Inside useState
, num
will be calculated according to the following process:
update(100)
turnsnum
into100
update(num => num + 1)
changesnum
to100 + 1 = 101
update(num => num * 3)
changesnum
to101 * 3 = 303
That is, when App
component render
, num
is 303.
Therefore, the calculation of the state needs to collect the updates triggered by first, and then calculate them uniformly in
useState
.
For the above example, the updates are named u0~u2 respectively, and the calculation formula of the state is:
baseState -> u0 -> u1 -> u2 = newState
Changes brought by Concurrent
Concurrent
(concurrency) brings the React
to 06232ce710f25d, which is reflected in the state calculation . According to the scene that triggers the update, the update has different priorities (for example, the onClick
callback useEffect
triggered updates).
The difference that manifests in the computed state is that if an update has a low priority, it will be skipped.
Assuming that the priority of u1
in the above example is low, then when the App
component is render
, the formula for calculating the state of num
is:
// 其中u1因为优先级低,被跳过
baseState -> u0 -> u2 = newState
which is:
update(100)
changesnum
to100
update(num => num * 3)
changesnum
to100 * 3 = 300
Obviously this result is wrong.
Therefore, the logic of React
computing state under concurrent conditions will be more complicated. Specifically, multiple rounds of computation may be involved.
When calculating the state, if a update is skipped, the next calculation will continue from the skipped
update.
For example, in the above example, u1
is skipped. num
is 100 when u1
is skipped. The state 100 at this time, and all the updates after u1
and will be saved and participate in the next calculation.
In the example, u1
and u2
are saved.
The next update is as follows:
- The initial state is
100
,update(num => num + 1)
changesnum
to100 + 1 = 101
update(num => num * 3)
changesnum
to101 * 3 = 303
It can be seen that the final result 303 is the same as the React synchronized with , but it only needs render
twice.
synced React render
once and the result is 303.
concurrent React render
twice, the result is 300 (intermediate state), 303 (final state).
The difference between the old and the new Concurrent
From the above example, we found that the number of times of component render
is affected by how many updates of are skipped by . Actually, it may not only be render
twice, but many times.
In the old concurrent React , the priority is a timestamp called expirationTime
. The algorithm to compare whether the update should be skipped is as follows:
// 更新优先级是否小于render的优先级
if (updateExpirationTime < renderExpirationTime) {
// ...被跳过
} else {
// ...不跳过
}
Under this logic, as long as the priority is low, it will be skipped, which means one more render
.
In the new concurrent React , the priority is stored in the 31-bit binary .
for example:
const renderLanes = 0b0101;
u1.lane = 0b0001;
u2.lane = 0b0010;
Where renderLanes
is the priority specified in this update.
The function to compare priority is:
function isSubsetOfLanes(set, subset) {
return (set & subset) === subset;
}
in:
// true
isSubsetOfLanes(renderLanes, u1.lane)
// false
isSubsetOfLanes(renderLanes, u2.lane)
u1.lane
included in renderLanes
, which means this update has sufficient priority.
u2.lane
is not included in renderLanes
, which means that this update does not have enough priority and is skipped.
But the of the skipped update ( u2
in the example) by lane
will be reset to 0, ie:
u2.lane = 0b0000;
Apparently any lanes
contains 0:
// true
isSubsetOfLanes(renderLanes, 0)
So this update will definitely be processed next time. In other words, in the new version of concurrent React , is skipped due to the priority of , resulting in repeating render , at most 2 times.
Summarize
Compared with the old version of concurrent React , the new version of concurrent React will have more advantages in render
times.
Reflected on the user's senses, the user will see less of the intermediate state is not fully .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。