React use源码解析

这篇文章发表于 阅读 19

use是React的一个hook,可以用来读取Promisecontext的值。

具体的用法和例子可以查看官方文档

use源码

use没有状态 ,所以不用将像useState那样创建hook对象,根据useable来判断是Promise还是context

function use<T>(usable: Usable<T>): T { if (usable !== null && typeof usable === 'object') { // if (typeof usable.then === 'function') { // 如果是 thenable. const thenable: Thenable<T> = (usable: any); return useThenable(thenable); } else if (usable.$$typeof === REACT_CONTEXT_TYPE) { // 如果 context const context: ReactContext<T> = (usable: any); return readContext(context); } } throw new Error('An unsupported type was passed to use(): ' + String(usable)); }
//useThenable function useThenable<T>(thenable: Thenable<T>): T { // Track the position of the thenable within this fiber. const index = thenableIndexCounter; thenableIndexCounter += 1; if (thenableState === null) { thenableState = createThenableState(); } // 获取promise的value const result = trackUsedThenable(thenableState, thenable, index); // 。。。。 return result; }

trackUsedThenable主要判断Promise的状态:

  1. Promise状态已经改变:如果状态是fulfilled,返回value;如果是rejected,抛出错误原因;
  2. 如果状态在pending中,就添加成功和失败的回调处理函数,同时抛出SuspenseException错误,挂起渲染过程;等到Promise状态改变后继续渲染。
// trackUsedThenable export function trackUsedThenable<T>( thenableState: ThenableState, thenable: Thenable<T>, index: number, ): T { const trackedThenables = getThenablesFromState(thenableState); const previous = trackedThenables[index]; if (previous === undefined) { // 添加 thenable trackedThenables.push(thenable); } else { if (previous !== thenable) { thenable.then(noop, noop); thenable = previous; } } switch (thenable.status) { case 'fulfilled': { // 成功 const fulfilledValue: T = thenable.value; return fulfilledValue; } case 'rejected': { // 失败 const rejectedError = thenable.reason; checkIfUseWrappedInAsyncCatch(rejectedError); throw rejectedError; } default: { if (typeof thenable.status === 'string') { thenable.then(noop, noop); } else { const root = getWorkInProgressRoot(); // 。。。 const pendingThenable: PendingThenable<T> = (thenable: any); pendingThenable.status = 'pending'; // 添加fulfilled/rejected回调 pendingThenable.then( fulfilledValue => { if (thenable.status === 'pending') { const fulfilledThenable: FulfilledThenable<T> = (thenable: any); fulfilledThenable.status = 'fulfilled'; fulfilledThenable.value = fulfilledValue; } }, (error: mixed) => { if (thenable.status === 'pending') { const rejectedThenable: RejectedThenable<T> = (thenable: any); rejectedThenable.status = 'rejected'; rejectedThenable.reason = error; } }, ); } // Check one more time in case the thenable resolved synchronously. // 再次检查Promise状态 switch (thenable.status) { case 'fulfilled': { const fulfilledThenable: FulfilledThenable<T> = (thenable: any); return fulfilledThenable.value; } case 'rejected': { const rejectedThenable: RejectedThenable<T> = (thenable: any); const rejectedError = rejectedThenable.reason; checkIfUseWrappedInAsyncCatch(rejectedError); throw rejectedError; } } // Suspend. // 如果是pending状态,就通过 SuspenseException错误, 挂起该fiber, // 在ReactFiberWorkLoop.js文件中的 renderRootSync/renderRootConcurrent中通过 try-catch来捕获SuspenseException错误 suspendedThenable = thenable; throw SuspenseException; } } }

如果render中抛出错误,就会在renderRootConcurrent中catch住,然后通过handleThrow找到对应的错误原因workInProgressSuspendedReasonSuspendedOnData,下一次循环时在switch/case中命中SuspendedOnData分支,添加thenable.then(onResolution, onResolution),Promise状态改变后就从该组件开始继续向后渲染。

function renderRootConcurrent(root: FiberRoot, lanes: Lanes) { // .... outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && workInProgress !== null ) { const unitOfWork = workInProgress; const thrownValue = workInProgressThrownValue; resumeOrUnwind: switch (workInProgressSuspendedReason) { // ... case SuspendedOnData: { // workInProgressSuspendedReason = SuspendedOnData 是在 handleThrow 中设置的 const thenable: Thenable<mixed> = (thrownValue: any); if (isThenableResolved(thenable)) { workInProgressSuspendedReason = NotSuspended; workInProgressThrownValue = null; replaySuspendedUnitOfWork(unitOfWork); break; } const onResolution = () => { if (workInProgressSuspendedReason === SuspendedOnData && workInProgressRoot === root) { // Promise回调已执行,可以继续render挂起的fiber workInProgressSuspendedReason = SuspendedAndReadyToContinue; } ensureRootIsScheduled(root); }; // 给thenable添加回调,在Promise决议后改变状态 thenable.then(onResolution, onResolution); break outer; } case SuspendedAndReadyToContinue: { const thenable: Thenable<mixed> = (thrownValue: any); if (isThenableResolved(thenable)) { // 重新render挂起的节点 workInProgressSuspendedReason = NotSuspended; workInProgressThrownValue = null; replaySuspendedUnitOfWork(unitOfWork); } else { // Otherwise, unwind then continue with the normal work loop. workInProgressSuspendedReason = NotSuspended; workInProgressThrownValue = null; throwAndUnwindWorkLoop(root, unitOfWork, thrownValue); } break; } // 。。。 } } if (__DEV__ && ReactCurrentActQueue.current !== null) { // ... } else { workLoopConcurrent(); } break; } catch (thrownValue) { // 这里catch了 use hook的pending状态,在下一次循环是判断 workInProgressSuspendedReason 来命中 SuspendedOnData,给promise添加回调,在决议后开始调度 handleThrow(root, thrownValue); } } while (true); // 。。。。 }