This article is published under a SIGNATURE 4.0 International (CC BY 4.0) license.

Author: Baiying front-end team @0O O0 first published at juejin.cn/post/708346…

directory

  • preface
  • UseDeferredValue for the first time
  • The realization of the useDeferredValue
  • conclusion

preface

Last week, the official release of Act18 was released on March 29th, and many of you have already started to experience the new features provided by version 18. The most notable of the new releases is the Concurrent mode. Updating the corresponding Reconcile process in Concurrent mode is interruptible, which allows the browser rendering process not to be blocked by a long reconcile process that causes the page to stall during interaction and allows higher-priority updates to be processed first.

In React18’s own choke throttle-UseTransition, we learned that triggering Concurrent mode requires the useTransition. In addition to useTransition, Act18 provides another API, useDeferredValue, which can also trigger Concurrent mode.

Today, we’re going to talk about useDeferredValue.

UseDeferredValue for the first time

For useDeferredValue, React18’s Changelog looks like this:

useDeferredValue lets you defer re-rendering a non-urgent part of the tree. It is similar to debouncing, but has a few advantages compared to it. There is no fixed time delay, so React will attempt the deferred render right after the first render is reflected on the screen. The deferred render is interruptible and doesn’t block user input.

UseDeferredValue allows us to defer rendering non-emergent parts of the browser, similar to anti-shake but with no fixed delay time. Deferred rendering starts after emergent parts appear on the browser screen first, and interrupts without blocking user input.

UseDeferredValue can be a bit confusing to understand, but let’s use a few simple demos to show you how to use it.

The sample a

Demo-usedeferredvalue-demo-1 is a simple demo-useDeferredValue-Demo-1 to simulate a Select component with search.

In the example above, we use an input box and a long list to simulate the Select component with search. The input box is controlled and the list has 50,000 nodes. In the example, we can see that the page is noticeably stuck throughout the interaction because the Reconcile process of 50,000 nodes takes up a lot of time for the JS engine, resulting in the rendering engine being blocked.

Typically, users want to see what they typed right away, and the results of the query can be displayed later. Based on this, we can use useDeferredValue to optimize, prioritizing input updates as urgent parts and deferring long list updates as non-urgent parts.

Example 2

In useDeferredValue – Demo-2, we use useDeferredValue to delay long list updates.

Looking at the example above, we noticed that using useDeferredValue didn’t improve the interaction with the input field, and there was still a noticeable lag in input and deletion.

Why is that? Why is useDeferredValue not in effect?

Let’s analyze a wave of reasons, 😂.

In the example above, we used useDeferredValue, but it didn’t work. We only deferred the deferredValues required by the long list, and the coordination process for the long list was not delayed. When input updates start to coordinate, there are still 50,000 nodes to process, which takes up a lot of time for the JS engine and clogs the rendering engine.

To make useDeferredValue work as it should, we can split the long list into a Memo component and pass the deferredValue to the long list component via props. When deferredValue changes, the long list component starts to update.

Example 3

In useDeferredValue-Demo-3, we split the long list into a memo component, which looks like this:

Looking at the example, we can see that the entire input interaction flows much more smoothly when the long list is separated into the Memo component. In the whole page interaction process, input update is a high priority update, priority processing; With useDeferredValue, long list updates become low-priority, delayed, and interruptable.

The realization of the useDeferredValue

Now that you know how to use useDeferredValue, let’s look at how useDeferredValue is implemented.

From the introduction of useDeferredValue in the previous section, we can extract a few key points: non-urgent/urgent, deferred, interruptible. Not urgent/urgent, meaning the update priority is low/high; Interruptible, meaning Concurrent mode; Delayed, which means updates need to be triggered asynchronously.

Based on this, we can use useTransition and useEffect to achieve an effect similar to useDeferredValue, as shown in useDeferredValue-demo-3:

The whole process can be summarized as follows:

  1. Initialize value and deferredValue to null.
  2. Enter 1 to trigger an input update by calling setValue;
  3. Input updates start processing, value changes to 1, and deferredValue remains empty.
  4. UseEffect dependencies change and callback needs to be handled. The long list props has not changed and does not need to be processed;
  5. The useEffect callback triggers the update of deferredValue.
  6. Long list updates start processing;

In fact, useDeferredValue is implemented in the React18 source code in the same way as in our example above:

UseDeferredValue function mountDeferredValue(value) {define a deferredValue var _mountState = mountState(value), prevValue = _mountState[0], setValue = _mountState[1]; UseEffect mountEffect(function () {// Change the context of setValue to transition, UseTransition var prevTransition = ReactCurrentBatchConfig$2.transition; ReactCurrentBatchConfig$2.transition = {}; try { setValue(value); } finally { ReactCurrentBatchConfig$2.transition = prevTransition; } }, [value]); // Return the old value prevValue; }Copy the code
UseDeferredValue function updateDeferredValue(value) {var _updateState = updateState(), prevValue = _updateState[0], setValue = _updateState[1]; UseEffect updateEffect(function () {var prevTransition = ReactCurrentBatchConfig$2.transition; ReactCurrentBatchConfig$2.transition = {}; try { setValue(value); } finally { ReactCurrentBatchConfig$2.transition = prevTransition; } }, [value]); // Return the old value prevValue; }Copy the code

conclusion

This is the end of useDeferredValue, and I believe you have a basic understanding of useDeferredValue. If you think this article is good, please give me a thumbs up at 😄.