React useDeferredValue源码

更新于 阅读 77

useDeferredValue是一个React hook,用于延迟更新UI。

可以使用在数据更新但UI可以延迟渲染的地方,如果状态变化会触发大量渲染时,useDeferredValue可以减少UI的渲染,避免卡顿。

mountDeferredValue

useDeferredValue会中会进行新旧value的比较,所以需要创建一个hook对象。

function mountDeferredValue<T>(value: T, initialValue?: T): T {
  const hook = mountWorkInProgressHook();
  return mountDeferredValueImpl(hook, value, initialValue);
}

在mount阶段直接显示了value,不会延迟。

// mountDeferredValueImpl function mountDeferredValueImpl<T>(hook: Hook, value: T, initialValue?: T): T { if ( enableUseDeferredValueInitialArg && initialValue !== undefined && !includesSomeLane(renderLanes, DeferredLane) ) { // ...... } else { hook.memoizedState = value; return value; } }

updateDeferredValue

在update阶段,会从上一次渲染的hook中拿到value,与新的value进行比较。

function updateDeferredValue<T>(value: T, initialValue?: T): T { const hook = updateWorkInProgressHook(); const resolvedCurrentHook: Hook = (currentHook: any); const prevValue: T = resolvedCurrentHook.memoizedState; return updateDeferredValueImpl(hook, prevValue, value, initialValue); }

updateDeferredValueImpl的更新步骤:

  1. 比较value:如果value没有发生变化,直接返回;
  2. 比较lanes:
    1. 如果本次渲染的renderLanes中包含高优先级,跳过value的更新,合并deferredLanes到当前fiber的lanes上,返回旧值;
    2. 如果本次渲染包含低优先级,更新并返回value。
// updateDeferredValueImpl
function updateDeferredValueImpl<T>(
  hook: Hook,
  prevValue: T,
  value: T,
  initialValue?: T,
): T {
  if (is(value, prevValue)) {
   // 如果value没有变化,直接返回
    return value;
  } else {
    //....

    const shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);
    // 如果renderLanes中不只包含非紧急的lanes(都是高优先级),就延迟更新value,返回preValue
    if (shouldDeferValue) {
      
      // 获取deferLane,lane分配逻辑与useTransition的分配相同
      const deferredLane = requestDeferredLane();
      currentlyRenderingFiber.lanes = mergeLanes(
        currentlyRenderingFiber.lanes,
        deferredLane,
      );
      // 记录被跳过的lanes,会被添加到root.pendingLanes上,会重新被渲染一次
      markSkippedUpdateLanes(deferredLane);

      return prevValue;
    } else {
      // 存在非紧急更新lanes时显示最新值时,更新value
      
      // Mark this as an update to prevent the fiber from bailing out.
      // markWorkInProgressReceivedUpdate 主要用于避免fiber复用(标记didReceiveUpdate=true),可以在ReactFiberBeginWork.js文件的 updateFunctionComponent函数中看到didReceiveUpdate的使用。
      markWorkInProgressReceivedUpdate();
      // 替换新值
      hook.memoizedState = value;
      return value;
    }
  }
}
标签:useDeferredValueReactmountDeferredValuedeferredLane