ronantakizawa/codereview-bench huggingface dataset:
Repository: facebook/react
File: packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js (JavaScript)

Code before:
      }
    }

    const root = ReactDOMClient.createRoot(container);
    await act(async () => {
      root.render(<Parent value="foo" />);
    });
    expect(portal2.textContent).toBe('foo');
  });

  it('fails gracefully when mixing React 15 and 16', async () => {
    class C extends React.Component {
      render() {
        return <div />;
      }
    }

    const container = document.createElement('div');
    const root = ReactDOMClient.createRoot(container);
    const componentRef = React.createRef();
    await act(async () => {
      root.render(<C ref={componentRef} />);
    });
    // React 15 calls this:
    // https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
    const component = componentRef.current;
    await expect(async () => {
      act(async () => {
        component._reactInternalInstance._processChildContext({});
      });
    }).toThrow(
      __DEV__
        ? '_processChildContext is not available in React 16+. This likely ' +
            'means you have multiple copies of React and are attempting to nest ' +
            'a React 15 tree inside a React 16 tree using ' +
            "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
            'make sure you have only one copy of React (and ideally, switch to ' +
            'ReactDOM.createPortal).'
        : "Cannot read property '_processChildContext' of undefined",
    );
  });
});


Review comment (suggestion):
This doesn't work because `component._reactInternalInstance` is an empty object and the test is to call `_processChildContext({})` which doesn't exist on `component._reactInternalInstance`

Code after:
        return <div>{this.context.value}</div>;
      }
    }

    const root = ReactDOMClient.createRoot(container);
    await act(async () => {
      root.render(<Parent value="foo" />);
    });
    expect(portal2.textContent).toBe('foo');
  });

  it('legacy test: fails gracefully when mixing React 15 and 16', () => {
    class C extends React.Component {
      render() {
        return <div />;
      }
    }
    const c = ReactDOM.render(<C />, document.createElement('div'));
    // React 15 calls this:
    // https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
    expect(() => {
      c._reactInternalInstance._processChildContext({});
    }).toThrow(
      __DEV__
        ? '_processChildContext is not available in React 16+. This likely ' +
            'means you have multiple copies of React and are attempting to nest ' +
            'a React 15 tree inside a React 16 tree using ' +
            "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
            'make sure you have only one copy of React (and ideally, switch to ' +
            'ReactDOM.createPortal).'
        : "Cannot read property '_processChildContext' of undefined",
    );
  });
});
Repository: facebook/react
File: scripts/flags/flags.js (JavaScript)

Code before:
        null,
      ],
      default: null,
    },
    sort: {
      alias: 's',
      describe: 'sort diff by one or more flags.',
      requiresArg: false,
      type: 'array',
      default: 'next',
      choices: [
        'www',
        'www-modern',
        'rn',
        'rn-fb',
        'canary',
        'next',
        'experimental',
      ],
    },
  }).argv;

// Load ReactNativeFeatureFlags with __NEXT_MAJOR__ replace with 'next'.
// We need to do string replace, since the __NEXT_MAJOR__ is assigned to __EXPERIMENTAL__.
function getReactNativeFeatureFlagsMajor() {
  const virtualName = 'ReactNativeFeatureFlagsMajor.js';
  const file = fs.readFileSync(
    path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'),
    'utf8'
  );
  const fileContent = transformSync(
    file.replace(
      'const __NEXT_MAJOR__ = __EXPERIMENTAL__;',
      'const __NEXT_MAJOR__ = "next";'
    ),
    {
      plugins: ['@babel/plugin-transform-modules-commonjs'],
    }
  ).code;

  const parent = module.parent;
  const m = new Module(virtualName, parent);
  m.filename = virtualName;

  m._compile(fileContent, virtualName);

  return m.exports;
}

// The RN and www Feature flag files import files that don't exist.
// Mock the imports with the dynamic flag values.

Review comment (suggestion):
I had to update the script since `__NEXT_MAJOR__` is no longer a global. Since it's inline, we need to string replace the assignment in the module and virtually require the file.

Code after:
        null,
      ],
      default: null,
    },
    sort: {
      alias: 's',
      describe: 'sort diff by one or more flags.',
      requiresArg: false,
      type: 'string',
      default: 'flag',
      choices: [
        'flag',
        'www',
        'www-modern',
        'rn',
        'rn-fb',
        'canary',
        'next',
        'experimental',
      ],
    },
  }).argv;

// Load ReactNativeFeatureFlags with __NEXT_MAJOR__ replace with 'next'.
// We need to do string replace, since the __NEXT_MAJOR__ is assigned to __EXPERIMENTAL__.
function getReactNativeFeatureFlagsMajor() {
  const virtualName = 'ReactNativeFeatureFlagsMajor.js';
  const file = fs.readFileSync(
    path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'),
    'utf8'
  );
  const fileContent = transformSync(
    file.replace(
      'const __NEXT_MAJOR__ = __EXPERIMENTAL__;',
      'const __NEXT_MAJOR__ = "next";'
    ),
    {
      plugins: ['@babel/plugin-transform-modules-commonjs'],
    }
  ).code;

  const parent = module.parent;
  const m = new Module(virtualName, parent);
  m.filename = virtualName;

  m._compile(fileContent, virtualName);

  return m.exports;
}

// The RN and www Feature flag files import files that don't exist.
Repository: facebook/react
File: packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js (JavaScript)

Code before:
        expect(clearLog()).toEqual([
          "Capitalize 'hello'",
          0,
          1,
          2,
          // `capitalizedText` only recomputes when the text has changed
          "Capitalize 'hello, world.'",
          3,
          'HELLO, WORLD.',
        ]);
        expect(domNode.tagName).toEqual('SPAN');
        expect(domNode.textContent).toEqual('HELLO, WORLD.');
      },
    );

    itRenders('with a warning for useState inside useMemo', async render => {
      function App() {
        useMemo(() => {
          useState();
          return 0;
        });
        return 'hi';
      }
      const domNode = await render(
        <App />,
        render === clientRenderOnBadMarkup
          ? // Leads to a hydration mismatch where retry and therefore log the warning again.
            6
          : 1,
      );
      expect(domNode.textContent).toEqual('hi');
    });

    itRenders('with a warning for useRef inside useState', async render => {
      function App() {
        const [value] = useState(() => {
          useRef(0);
          return 0;
        });
        return value;
      }

      const domNode = await render(
        <App />,
        render === clientRenderOnBadMarkup
          ? // Leads to a hydration mismatch where retry and therefore log the warning again.
            6
          : 1,
      );
      expect(domNode.textContent).toEqual('0');
    });

Review comment (suggestion):
We use this pattern in other tests as well. E.g. `packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js` makes extensive usage of assertions based on the renderer.

Code after:
        expect(clearLog()).toEqual([
          "Capitalize 'hello'",
          0,
          1,
          2,
          // `capitalizedText` only recomputes when the text has changed
          "Capitalize 'hello, world.'",
          3,
          'HELLO, WORLD.',
        ]);
        expect(domNode.tagName).toEqual('SPAN');
        expect(domNode.textContent).toEqual('HELLO, WORLD.');
      },
    );

    itRenders('with a warning for useState inside useMemo', async render => {
      function App() {
        useMemo(() => {
          useState();
          return 0;
        });
        return 'hi';
      }
      const domNode = await render(
        <App />,
        render === clientRenderOnBadMarkup
          ? // On hydration mismatch we retry and therefore log the warning again.
            2
          : 1,
      );
      expect(domNode.textContent).toEqual('hi');
    });

    itRenders('with a warning for useRef inside useState', async render => {
      function App() {
        const [value] = useState(() => {
          useRef(0);
          return 0;
        });
        return value;
      }

      const domNode = await render(
        <App />,
        render === clientRenderOnBadMarkup
          ? // On hydration mismatch we retry and therefore log the warning again.
            2
          : 1,
      );
      expect(domNode.textContent).toEqual('0');
    });
Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
  return primitiveStackCache;
}

let currentFiber: null | Fiber = null;
let currentHook: null | Hook = null;

function nextHook(): null | Hook {
  const hook = currentHook;
  if (hook !== null) {
    currentHook = hook.next;
  }
  return hook;
}

function readContext<T>(context: ReactContext<T>): T {
  // For now we don't expose readContext usage in the hooks debugging info.
  return context._currentValue;
}

function use<T>(usable: Usable<T>): T {
  if (usable !== null && typeof usable === 'object') {
    // $FlowFixMe[method-unbinding]
    if (typeof usable.then === 'function') {
      // TODO: What should this do if it receives an unresolved promise?
      throw new Error(
        'Support for `use(Promise)` not yet implemented in react-debug-tools.',
      );
    } else if (
      usable.$$typeof === REACT_CONTEXT_TYPE ||
      usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
    ) {
      const context: ReactContext<T> = (usable: any);
      const value = readContext(context);

      hookLog.push({
        primitive: 'Use',
        stackError: new Error(),
        value,
      });

      return value;
    }
  }

  // eslint-disable-next-line react-internal/safe-string-coercion
  throw new Error('An unsupported type was passed to use(): ' + String(usable));
}

function useContext<T>(context: ReactContext<T>): T {
  hookLog.push({
    primitive: 'Context',

Review comment (suggestion):
Because we patch Dispatcher with these hooks, we can't really return something other than `useThenable(usable)` here.

We could display the user some string like `Promise<rejected | resolved>`, but don't support it atm. 

Code after:
}

let currentFiber: null | Fiber = null;
let currentHook: null | Hook = null;

function nextHook(): null | Hook {
  const hook = currentHook;
  if (hook !== null) {
    currentHook = hook.next;
  }
  return hook;
}

function readContext<T>(context: ReactContext<T>): T {
  // For now we don't expose readContext usage in the hooks debugging info.
  return context._currentValue;
}

function use<T>(usable: Usable<T>): T {
  if (usable !== null && typeof usable === 'object') {
    // $FlowFixMe[method-unbinding]
    if (typeof usable.then === 'function') {
      // TODO: What should this do if it receives an unresolved promise?
      throw new Error(
        'Support for `use(Promise)` not yet implemented in react-debug-tools.',
      );
    } else if (usable.$$typeof === REACT_CONTEXT_TYPE) {
      const context: ReactContext<T> = (usable: any);
      const value = readContext(context);

      hookLog.push({
        primitive: 'Use',
        stackError: new Error(),
        value,
      });

      return value;
    }
  }

  // eslint-disable-next-line react-internal/safe-string-coercion
  throw new Error('An unsupported type was passed to use(): ' + String(usable));
}

function useContext<T>(context: ReactContext<T>): T {
  hookLog.push({
    primitive: 'Context',
    stackError: new Error(),
    value: context._currentValue,
  });
  return context._currentValue;
Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
        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,
  useCacheRefresh,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useInsertionEffect,
  useMemo,
  useMemoCache,
  useOptimistic,
  useReducer,
  useRef,
  useState,
  useTransition,
  useSyncExternalStore,
  useDeferredValue,

Review comment (suggestion):
Followed the same pattern as https://github.com/facebook/react/pull/28297.

Do we actually reach this with `useFormStatus`? Asking mainly because `SuspenseException` currently includes a message that's specific to `use`

Code after:
          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
  // i.e. we only reach this point with Awaited<S>
  const state = ((value: any): Awaited<S>);
  return [state, (payload: P) => {}];
}

const Dispatcher: DispatcherType = {
  use,
  readContext,
  useCacheRefresh,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useInsertionEffect,
  useMemo,
Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
  }
  hookLog.push({
    primitive: 'FormState',
    stackError: new Error(),
    value: state,
    debugInfo: null,
  });
  return [state, (payload: P) => {}];
}

function useHostTransitionStatus(): TransitionStatus {
  // The type is host specific. This is just a placeholder.
  let status: mixed = null;

  if (currentFiber !== null) {
    const dependencies = currentFiber.dependencies;
    if (dependencies !== null) {
      let contextDependency = dependencies.firstContext;
      while (contextDependency !== null) {
        if (
          // $FlowFixMe -- TODO
          contextDependency.context.$$contextType ===
          Symbol.for('react.HostTransitionContext')
        ) {
          // TODO: This as well as contextDependency.context.currentValue is null because setupContext does not set the value yet
          status = contextDependency.memoizedValue;
        }
        contextDependency = contextDependency.next;
      }
    }
  }

  // TODO: Handle host transition context not found.

  hookLog.push({
    primitive: 'HostTransitionStatus',
    stackError: new Error(),
    value: status,
    debugInfo: null,
  });

  return ((status: any): TransitionStatus);
}

const Dispatcher: DispatcherType = {
  use,
  readContext,
  useCacheRefresh,
  useCallback,
  useContext,
  useEffect,

Review comment (suggestion):
Will rebase this onto https://github.com/facebook/react/pull/28467 and then we just read from `currentContextDependency.memoizedValue`

Code after:
    debugInfo: null,
    dispatcherHookName: 'DeferredValue',
  });
  return prevValue;
}

function useId(): string {
  const hook = nextHook();
  const id = hook !== null ? hook.memoizedState : '';
  hookLog.push({
    displayName: null,
    primitive: 'Id',
    stackError: new Error(),
    value: id,
    debugInfo: null,
    dispatcherHookName: 'Id',
  });
  return id;
}

// useMemoCache is an implementation detail of Forget's memoization
// it should not be called directly in user-generated code
function useMemoCache(size: number): Array<any> {
  const fiber = currentFiber;
  // Don't throw, in case this is called from getPrimitiveStackCache
  if (fiber == null) {
    return [];
  }

  // $FlowFixMe[incompatible-use]: updateQueue is mixed
  const memoCache = fiber.updateQueue?.memoCache;
  if (memoCache == null) {
    return [];
  }

  let data = memoCache.data[memoCache.index];
  if (data === undefined) {
    data = memoCache.data[memoCache.index] = new Array(size);
    for (let i = 0; i < size; i++) {
      data[i] = REACT_MEMO_CACHE_SENTINEL;
    }
  }

  // We don't write anything to hookLog on purpose, so this hook remains invisible to users.

  memoCache.index++;
  return data;
}

function useOptimistic<S, A>(
  passthrough: S,
Repository: facebook/react
File: packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegrationDOM-test.js (JavaScript)

Code before:
        </form>,
      );
    });

    // Implementation detail. Feel free to adjust the position of the Fiber in the tree.
    const formStatusFiber = root._internalRoot.current.child.child;
    const treeWithFiber = ReactDebugTools.inspectHooksOfFiber(formStatusFiber);
    expect(normalizeSourceLoc(treeWithFiber)).toEqual([
      {
        debugInfo: null,
        hookSource: {
          columnNumber: 0,
          fileName: '**',
          functionName: 'FormStatus',
          lineNumber: 0,
        },
        id: null,
        isStateEditable: false,
        name: '.useFormStatus',
        subHooks: [
          {
            debugInfo: null,
            hookSource: {
              columnNumber: 0,
              fileName: '**',
              functionName: 'Object.useFormStatus',
              lineNumber: 0,
            },
            id: null,
            isStateEditable: false,
            name: 'HostTransitionStatus',
            subHooks: [],
            value: null,
          },
        ],
        value: null,
      },
      {
        debugInfo: null,
        hookSource: {
          columnNumber: 0,
          fileName: '**',
          functionName: 'FormStatus',
          lineNumber: 0,
        },
        id: 0,
        isStateEditable: false,
        name: 'Memo',
        subHooks: [],
        value: 'memo',
      },

Review comment (bug):
In the build tests in prod this comes out as `Object.useHostTransitionStatus [as useFormStatus]`: https://app.circleci.com/pipelines/github/facebook/react/50936/workflows/b211c172-f33c-4e9f-806e-0eec46dcd88c/jobs/796848?invite=true#step-106-1107_84

This trips up debug-tools: all subsequent hooks (even built-in ones) are consider sub hooks. 

The source map is not adding the `[as ...]` since even without a source map I get `"functionName": "Object.ia [as useFormStatus]",`. Either way, the mapping from `ia` to `useHostTransitionStatus` is wrong.



Code after:
        id: null,
        isStateEditable: false,
        name: 'FormStatus',
        subHooks: [],
        value: null,
      },
      {
        debugInfo: null,
        hookSource: {
          columnNumber: 0,
          fileName: '**',
          functionName: 'FormStatus',
          lineNumber: 0,
        },
        id: 0,
        isStateEditable: false,
        name: 'Memo',
        subHooks: [],
        value: 'memo',
      },
      {
        debugInfo: null,
        hookSource: {
          columnNumber: 0,
          fileName: '**',
          functionName: 'FormStatus',
          lineNumber: 0,
        },
        id: 1,
        isStateEditable: false,
        name: 'Memo',
        subHooks: [],
        value: 'not used',
      },
    ]);
  });
});
Repository: facebook/react
File: packages/react-debug-tools/src/ReactDebugHooks.js (JavaScript)

Code before:
  const state = ((value: any): Awaited<S>);

  // TODO: support displaying pending value
  return [state, (payload: P) => {}, false];
}

function useHostTransitionStatus(): TransitionStatus {
  const status = readContext<TransitionStatus>(
    // $FlowFixMe[prop-missing] `readContext` only needs _currentValue
    ({
      // $FlowFixMe[incompatible-cast] TODO: Incorrect bottom value without access to Fiber config.
      _currentValue: null,
    }: ReactContext<TransitionStatus>),
  );

  hookLog.push({
    displayName: null,
    primitive: 'HostTransitionStatus',
    stackError: new Error(),
    value: status,
    debugInfo: null,
    dispatcherHookName: 'HostTransitionStatus',
    wrapperNames: [
      // react-dom
      'FormStatus',
    ],
  });

  return status;
}

const Dispatcher: DispatcherType = {
  use,
  readContext,
  useCacheRefresh,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useInsertionEffect,
  useMemo,
  useMemoCache,
  useOptimistic,
  useReducer,
  useRef,
  useState,
  useTransition,
  useSyncExternalStore,
  useDeferredValue,

Review comment (suggestion):
I wonder if we actually need to support renderer-specific hooks. Same as for hooks from core, we relying on the hook name, but at least we have one core. Here, I am assuming, if we had React Native renderer, we would also treat a hook named `useFormStatus` as primitive React hook.

Code after:
const Dispatcher: DispatcherType = {
  use,
  readContext,
  useCacheRefresh,
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useInsertionEffect,
  useMemo,
  useMemoCache,
  useOptimistic,
  useReducer,
  useRef,
  useState,
  useTransition,
  useSyncExternalStore,
  useDeferredValue,
  useId,
  useFormState,
  useActionState,
  useHostTransitionStatus,
};

// create a proxy to throw a custom error
// in case future versions of React adds more hooks
const DispatcherProxyHandler = {
  get(target: DispatcherType, prop: string) {
    if (target.hasOwnProperty(prop)) {
      return target[prop];
    }
    const error = new Error('Missing method in Dispatcher: ' + prop);
    // Note: This error name needs to stay in sync with react-devtools-shared
    // TODO: refactor this if we ever combine the devtools and debug tools packages
    error.name = 'ReactDebugToolsUnsupportedHookError';
    throw error;
  },
};

// `Proxy` may not exist on some platforms
const DispatcherProxy =
  typeof Proxy === 'undefined'
    ? Dispatcher
    : new Proxy(Dispatcher, DispatcherProxyHandler);

// Inspect

export type HookSource = {
  lineNumber: number | null,