Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 20, 2022 09:30 am GMT

React: How does useDeferredValue and useTransition work ?

As we know that Concurrent was enabled by default in React 18.

To implement this feature, there are new 2 hooks that come into the game: useDeferredValue and useTransition

I. useDeferredValue

1. Mount phase:

In mount phase (first render), useDeferredValue will be treated as below :

function mountDeferredValue<T>(value: T): T {  const hook = mountWorkInProgressHook();  hook.memoizedState = value;  return value;}

View Source

This mean it will save the current value into memoizedState and directly return it.

So on first render, deferredValue will be the same as input value.

2. Update phase:

When component have an update, useDeferredValue will be treated as below:

function updateDeferredValue<T>(value: T): T {  const hook = updateWorkInProgressHook();  const resolvedCurrentHook: Hook = (currentHook: any);  const prevValue: T = resolvedCurrentHook.memoizedState;  return updateDeferredValueImpl(hook, prevValue, value);}
function updateDeferredValueImpl<T>(hook: Hook, prevValue: T, value: T): T {  const shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);  if (shouldDeferValue) {    if (!is(value, prevValue)) {      const deferredLane = claimNextTransitionLane();      currentlyRenderingFiber.lanes = mergeLanes(        currentlyRenderingFiber.lanes,        deferredLane,      );      markSkippedUpdateLanes(deferredLane);      hook.baseState = true;    }    return prevValue;  } else {    if (hook.baseState) {      hook.baseState = false;      markWorkInProgressReceivedUpdate();    }    hook.memoizedState = value;    return value;  }}

View Source

  1. in updateDeferredValue, it will get the previos value that was already stored in memoizedState and put the previos value and current value as parameter to next function updateDeferredValueImpl

  2. in updateDeferredValueImpl, there will be a check if the current lanes contain a UrgentLanes

const shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);

Dig into includesOnlyNonUrgentLanes we will know CurrentLanes are :

SyncLane | InputContinuousLane | DefaultLane;

So we can see that if current process contain a UrgentLane it will skip update DeferredValue and return the previos value immediately after schedule a render with Transition Lane (not prioritized).

Otherwise if current process contain only nonUrgentLanes, it will memoize and update the new value on useDeferredValue.

The flow is simply as below diagram:

image

II. useTransition

1. Mount phase

function mountTransition(): [  boolean,  (callback: () => void, options?: StartTransitionOptions) => void,] {  const [isPending, setPending] = mountState(false);  // The `start` method never changes.  const start = startTransition.bind(null, setPending);  const hook = mountWorkInProgressHook();  hook.memoizedState = start;  return [isPending, start];}

View Source

Interesting, in mount phase, useTransition will use useState (mountState) to store its state "isPending".

Next, it will generate start function by bind setPending (dispatch setState) into startTransition, then put this function into hook. And return to the component.

Let's see what's startTransition do (I've removed all comments and warning code to make function shorter) :

function startTransition(setPending, callback) {  const previousPriority = getCurrentUpdatePriority();  setCurrentUpdatePriority(    higherEventPriority(previousPriority, ContinuousEventPriority),  );  setPending(true);  const prevTransition = ReactCurrentBatchConfig.transition;  ReactCurrentBatchConfig.transition = 1;  try {    setPending(false);    callback();  } finally {    setCurrentUpdatePriority(previousPriority);    ReactCurrentBatchConfig.transition = prevTransition;  }}

So at first, it will ensure the current priority is higher than ContinuousEventPriority, then set isPending to true to mark as transition start.
After that, it will save current value of ReactCurrentBatchConfig.transition into prevTransition and then set it to 1.

Guess what, setting ReactCurrentBatchConfig.transition to 1 will force put next updates into Transition Lane.

const isTransition = requestCurrentTransition() !== NoTransition;  if (isTransition) {    if (currentEventTransitionLane === NoLane) {      currentEventTransitionLane = claimNextTransitionLane();    }    return currentEventTransitionLane;  }

You can check it in requestUpdateLane
After process callback, it will reset ReactCurrentBatchConfig into original by prevTransition variable.

2. Update phase

Update phase for useTransition is kind of simpler, since trigger function start was already memoized in hook.

function updateTransition(): [boolean, (() => void) => void] {  const [isPending] = updateState(false);  const hook = updateWorkInProgressHook();  const start = hook.memoizedState;  return [isPending, start];}

Conclusion:

By looking at their code, we can see that useTransition and useDeferredValue will basically put the process into Transition Lane.
Also, Concurrent does not mean React doing multi-task in the same time. It just mean React will process tasks with priority order.


Original Link: https://dev.to/linhbui167/how-does-usedeferredvalue-and-usetransition-work--31fp

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To