Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
  let debugInfo = null;
  if (hook !== null) {
    const actionResult = hook.memoizedState;
    if (
      typeof actionResult === 'object' &&
      actionResult !== null &&
      // $FlowFixMe[method-unbinding]
      typeof actionResult.then === 'function'
    ) {
      const thenable: Thenable<Awaited<S>> = (actionResult: any);
      switch (thenable.status) {
        case 'fulfilled': {
          state = thenable.value;
          debugInfo =
            thenable._debugInfo === undefined ? null : thenable._debugInfo;
          break;
        }
        case 'rejected': {
          const rejectedError = thenable.reason;
          throw rejectedError;
        }
        default:
          // If this was an uncached Promise we have to abandon this attempt
          // but we can still emit anything up until this point.
          hookLog.push({
            primitive: 'Unresolved',
            stackError: new Error(),
            value: thenable,
            debugInfo:
              thenable._debugInfo === undefined ? null : thenable._debugInfo,
          });
          throw SuspenseException;
      }
    } else {
      state = (actionResult: any);
    }
  } else {
    state = initialState;
  }
  hookLog.push({
    primitive: 'FormState',
    stackError: new Error(),
    value: state,
    debugInfo: debugInfo,
  });
  return [state, (payload: P) => {}];
}

const Dispatcher: DispatcherType = {
  use,
  readContext,

Review comment (bug):
This needs a different name and you need to exhaust this case in the stack frame cache. See https://github.com/facebook/react/blob/main/packages/react-debug-tools/src/ReactDebugHooks.js#L111-L117

Otherwise this will generate the wrong stack frame information and so it'll generate a messed up tree.

Probably should add some tests for it.

Code after:
  let debugInfo = null;
  let error = null;

  if (hook !== null) {
    const actionResult = hook.memoizedState;
    if (
      typeof actionResult === 'object' &&
      actionResult !== null &&
      // $FlowFixMe[method-unbinding]
      typeof actionResult.then === 'function'
    ) {
      const thenable: Thenable<Awaited<S>> = (actionResult: any);
      switch (thenable.status) {
        case 'fulfilled': {
          value = thenable.value;
          debugInfo =
            thenable._debugInfo === undefined ? null : thenable._debugInfo;
          break;
        }
        case 'rejected': {
          const rejectedError = thenable.reason;
          error = rejectedError;
          break;
        }
        default:
          // If this was an uncached Promise we have to abandon this attempt
          // but we can still emit anything up until this point.
          error = SuspenseException;
          debugInfo =
            thenable._debugInfo === undefined ? null : thenable._debugInfo;
          value = thenable;
      }
    } else {
      value = (actionResult: any);
    }
  } else {
    value = initialState;
  }

  hookLog.push({
    primitive: 'FormState',
    stackError: stackError,
    value: value,
    debugInfo: debugInfo,
  });

  if (error !== null) {
    throw error;
  }

  // value being a Thenable is equivalent to error being not null