Skip to content

fix[eslint-plugin-react-hooks]: detect memo/forwardRef-wrapped components in mayContainReactCode heuristic#36433

Open
michaeljaltamirano wants to merge 2 commits intofacebook:mainfrom
michaeljaltamirano:fix/memo-heuristic-variable-declaration
Open

fix[eslint-plugin-react-hooks]: detect memo/forwardRef-wrapped components in mayContainReactCode heuristic#36433
michaeljaltamirano wants to merge 2 commits intofacebook:mainfrom
michaeljaltamirano:fix/memo-heuristic-variable-declaration

Conversation

@michaeljaltamirano
Copy link
Copy Markdown

@michaeljaltamirano michaeljaltamirano commented May 7, 2026

Summary

This PR seeks to resolve #36432 by updating packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts to support const MyComponent = memo(function MyComponent() {}), const MyComponent = React.memo(() => {}), and const MyComponent = forwardRef(function MyComponent() {}) syntaxes.

eslint-plugin-react-hooks@7.1.0 introduced some logic to improve performance by skipping linting for files that do not return true for mayContainReactCode. This PR contains some extension of that logic to make sure we are linting files with the above syntax.

The scope specifically only concerns memo and forwardRef. This PR could be extended to include other react exports, but is specifically considering custom higher-order components out of scope.

How did you test this change?

Manually, by changing the source code of packages/eslint-plugin-react-hooks/src/shared/RunReactCompiler.ts directly to raise lint errors in a repo that was not reporting existing // eslint-disable-next-line directives, after upgrading eslint-plugin-react-hooks from 7.0.1 to 7.1.1. I double-checked that the compiler itself reports all such instances are errors (Compiler Playground link) and so this is specifically an eslint-plugin-react-hooks regression.

Per the PR template and https://legacy.reactjs.org/docs/how-to-contribute.html, I also manually ran the related yarn scripts to double-check these changes passed prior to opening a PR and running CI.

@meta-cla meta-cla Bot added the CLA Signed label May 7, 2026
…ents in mayContainReactCode heuristic

The VariableDeclaration handler in checkTopLevelNode only matched
ArrowFunctionExpression and FunctionExpression as initializers. This
caused the heuristic to return false for patterns like:

  const MyComponent = memo(function MyComponent() { ... })
  const MyComponent = React.memo(() => { ... })
  const MyComponent = forwardRef(function MyComponent() { ... })

When mayContainReactCode returns false, compilation is skipped and no
compiler-based rules (refs, set-state-in-effect, etc.) are reported —
even though the identical violations in a plain function declaration are
caught correctly.

Fix: unwrap one level of CallExpression arguments so that a function
literal passed to a HOC (memo, forwardRef, etc.) is treated the same as
a direct function assignment when the variable name matches the component
or hook naming convention.
…ed components

Add regression tests for memo/forwardRef-wrapped component patterns that
were silently skipped by the mayContainReactCode heuristic before the fix
to checkTopLevelNode's VariableDeclaration handler.

Each test file gains:
- invalid: memo(function Comp), memo(arrow), React.memo(function Comp),
           forwardRef(function Comp), export const Comp = memo(...)
  These prove compilation runs and violations are reported, matching the
  behavior of equivalent plain function declarations.
- valid: lowercase variable initialized with a wrapped function
  This proves the heuristic still correctly skips non-component files
  when the variable name does not match the PascalCase/hook naming convention.
@michaeljaltamirano michaeljaltamirano force-pushed the fix/memo-heuristic-variable-declaration branch from 4f9670f to ff12412 Compare May 7, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Compiler Bug]: eslint-plugin-react-hooks 7.1.0 skips linting components defined with in-line memo and forwardRef wrappers

1 participant