Skip to content

[compiler][rfc] Enable hook guards in dev mode by default#81

Open
everettbu wants to merge 1 commit into
mainfrom
pr32524
Open

[compiler][rfc] Enable hook guards in dev mode by default#81
everettbu wants to merge 1 commit into
mainfrom
pr32524

Conversation

@everettbu

@everettbu everettbu commented Dec 12, 2025

Copy link
Copy Markdown

Mirror of facebook/react#32524
Original author: mofeiZ


This validation ensures that React compiler-enabled apps remain correct. That is, code that errors with this validation is most likely invalid with React compiler is enabled (specifically, hook calls will be compiled to if-else memo blocks).

Hook guards are used extensively for Meta's react compiler rollouts. There, they're enabled for developers (for dev builds) and on e2e test runs. Let's enable by default for oss as well

Examples of inputs this rule throws on

  • Components should not be invoked directly as React Compiler could memoize the call to AnotherComponent, which introduces conditional hook calls in its compiled output.

    function Invalid1(props) {
     const myJsx = AnotherComponent(props);
     return <div> { myJsx } </div>;
    }
  • Hooks must be named as hooks. Similarly, hook calls may not appear in functions that are not components or hooks.

    const renamedHook = useState;
    function Invalid2() {
      const [state, setState] = renamedHook(0);
    }
    
    function Invalid3() {
      const myFunc = () => useContext(...);
      myFunc();
    }
  • Hooks must be directly called (from the body of a component or hook)

    function call(fn) {
      return fn();
    }
    
    function Invalid4() {
      const result = call(useMyHook);
    }
    

Example of hook guard error (in dev build)

image

This validation ensures that React compiler-enabled apps remain correct. That is, code that errors with this validation is most likely ***invalid*** with React compiler is enabled (specifically, hook calls will be compiled to if-else memo blocks).

Hook guards are used extensively for Meta's react compiler rollouts. There, they're enabled for developers (for dev builds) and on e2e test runs. Let's enable by default for oss as well

### Examples of inputs this rule throws on

* Components should not be invoked directly as React Compiler could memoize the call to AnotherComponent, which introduces conditional hook calls in its compiled output.
  ```js
  function Invalid1(props) {
   const myJsx = AnotherComponent(props);
   return <div> { myJsx } </div>;
  }
  ```
* Hooks must be named as hooks. Similarly, hook calls may not appear in functions that are not components or hooks.
  ```js
  const renamedHook = useState;
  function Invalid2() {
    const [state, setState] = renamedHook(0);
  }

  function Invalid3() {
    const myFunc = () => useContext(...);
    myFunc();
  }
  ```

* Hooks must be directly called (from the body of a component or hook)
  ```
  function call(fn) {
    return fn();
  }

  function Invalid4() {
    const result = call(useMyHook);
  }
  ```


### Example of hook guard error (in dev build)
<img width="1237" alt="image" src="https://github.com/user-attachments/assets/e9ada403-b0d7-4840-b6d5-ad600519c6e6" />
@everettbu everettbu added CLA Signed React Core Team Opened by a member of the React Core Team labels Dec 12, 2025
@greptile-apps

greptile-apps Bot commented Dec 12, 2025

Copy link
Copy Markdown

Greptile Overview

Greptile Summary

enabled hook guards in dev mode by default to validate that React compiler-enabled apps follow hook rules correctly

  • changed enableEmitHookGuards default configuration to enable guards in dev mode with devonly: true
  • added logic in BabelPlugin.ts to conditionally disable guards in production builds when devonly flag is set
  • expanded LazyGuardDispatcher to handle additional methods (unstable_useMemoCache, readContext, getCacheSignal, getCacheForType, use) that should bypass guard validation
  • updated compatibility layer in $dispatcherGuard to support both legacy (ReactCurrentDispatcher) and new (H) React internals
  • added null check for originalDispatcher to handle edge case where hooks are used as first-class values in inner functions
  • Critical bug found: line 93 in index.ts always calls useMemoCache instead of the correct dispatcher method from the loop variable

Confidence Score: 0/5

  • This PR cannot be merged - contains a critical logic bug that will cause runtime errors
  • The bug on line 93 of index.ts will cause all non-hook dispatcher methods (readContext, use, getCacheForType, etc.) to incorrectly call useMemoCache, resulting in runtime errors when these methods are invoked
  • Pay immediate attention to compiler/packages/react-compiler-runtime/src/index.ts line 93 - fix required before merge

Important Files Changed

File Analysis

Filename Score Overview
compiler/packages/react-compiler-runtime/src/index.ts 1/5 added multiple dispatcher methods to LazyGuardDispatcher, but critical bug on line 93 - always calls useMemoCache instead of the actual method from the key variable
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts 5/5 changed enableEmitHookGuards default to enable hook guards in dev mode, added devonly field to schema for backwards compatibility
compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts 5/5 added logic to disable hook guards in production when devonly is true, respecting the new configuration option

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

'React Compiler internal invariant violation: unexpected null dispatcher',
);
} else {
return (originalDispatcher as any).useMemoCache(...args);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: hardcoded useMemoCache call will fail for other dispatcher methods like readContext, use, getCacheForType, etc. - should call (originalDispatcher as any)[key](...args) instead

Suggested change
return (originalDispatcher as any).useMemoCache(...args);
return (originalDispatcher as any)[key](...args);
Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/react-compiler-runtime/src/index.ts
Line: 93:93

Comment:
**logic:** hardcoded `useMemoCache` call will fail for other dispatcher methods like `readContext`, `use`, `getCacheForType`, etc. - should call `(originalDispatcher as any)[key](...args)` instead

```suggestion
      return (originalDispatcher as any)[key](...args);
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants