React18 has entered the RC (release candidate) phase, only one step away from the official version.
Today, instead of talking about new features, we’ll talk about a detail that makes v18 better than the old version: v18 may have fewer component renders.
Where does the state come from?
In the following components.
App component render executes useState after rendering, which returns the latest value of num.
That is, the component must be rendered in order to know the latest state. Why is this the case?
Consider the following code that triggers an update.
The onClick execution triggers the update, which leads to the App component render, and then the useState execution.
Inside useState, the following process is followed to calculate num.
update(100)changes num to 100
update(num => num + 1)changes num to 100 + 1 = 101
update(num => num * 3)will change num to 101 * 3 = 303
That is, when the App component renders, num is 303.
So, the state calculation needs to collect the triggered updates first and then calculate them uniformly in useState.
For the above example, naming the updates as u0 to u2 respectively, the calculation formula for the state is
Concurrent brings changes
Concurrent brings the concept of priority to React, which is reflected in the state calculation, where updates have different priorities depending on the scenario that triggered them (for example, updates triggered in onClick callbacks have higher priority than updates triggered in useEffect callbacks).
The difference in the computed state is that if an update has a low priority, it will be skipped.
Assuming the low priority of u1 in the above example, the formula for calculating the num state when the App component is rendered is
update(100)changes num to 100
update(num => num * 3)changes num to 100 * 3 = 300
Obviously this result is not correct.
Therefore, the logic of React to compute state in concurrent cases will be more complex. Specifically, it may include multiple rounds of computation.
When computing state, if an update is skipped, the next computation will continue to compute from the skipped update backwards.
For example, in the above example, u1 is skipped. When u1 is skipped, num is 100, and the state 100, and all updates after u1, are saved for the next calculation.
In the example that is, u1 and u2 are saved.
The next update is as follows.
- initial state is 100,
update(num => num + 1) changes num to 100 + 1 = 101
update(num => num * 3)changes num to 101 * 3 = 303
As you can see, the final result 303 is the same as the synchronous React, except that it needs to be rendered twice.
Synchronous React render once, result is 303.
Concurrent React render twice, the result is 300 (intermediate state) and 303 (final state) respectively.
The difference between old and new Concurrent
From the above example we see that the number of times a component renders is affected by how many updates are skipped and may actually be rendered not just twice, but multiple times.
In the old version of Concurrent React, what indicates priority is a timestamp called expirationTime. The algorithm for comparing whether updates should be skipped is as follows.
In this logic, as long as the priority is low, it will be skipped, meaning one more render.
In the new version of concurrent React, priority is stored in a 31-bit binary number.
As an example.
where renderLanes is the priority specified for this update.
The function to compare priorities is
u1.lane is included in renderLanes, which means this update has sufficient priority.
u2.lane is not contained in renderLanes, which means this update does not have enough priority and is skipped.
But the lane of the skipped update (u2 in the example) will be reset to 0, i.e.
Obviously any lanes contain zeros.
So this update will definitely be processed the next time. In other words, in new versions of concurrent React, there will be at most 2 duplicate renders due to priority reasons being skipped.
Compared with the old concurrent React, the new concurrent React will have more advantages in the number of renders.
Reflecting on the user’s senses, the user will see less intermediate state that has not been fully computed.