Skip to content

[Compiler Bug]: react-hooks/preserve-manual-memoization is triggered for state setter depending on position of useState #34957

@kachkaev

Description

@kachkaev

What kind of issue is this?

  • React Compiler core (the JS output is incorrect, or your app works incorrectly after optimization)
  • babel-plugin-react-compiler (build issue installing or using the Babel plugin)
  • eslint-plugin-react-compiler eslint-plugin-react-hooks (build issue installing or using the eslint plugin)
  • react-compiler-healthcheck (build issue installing or using the healthcheck script)

Link to repro

https://playground.react.dev/#N4Igzg9grgTgxgUxALhASwLYAcIwC4AEAVAQIZgEBKCpchAZjBBgQDogw13sDcrAdgIQAPHPgL0o-Omgj8CAUWGlsAGwQAKYATwALNPwDmFAL4BKAsAEECcOWEJ6DhggF4d+o2AB09AwBMNJyM3AD4PZ29IDARgl3CABjM+QXlbe0IAbTspPAAaAjBYgGFofjwAXTcqLjxvKCKAZTxSPE0klOt0-gcCVVIAIwRVarjvfqHVbzwIABkIAHcEGGLyTWSurrsewl1Sfn91YtU0OABraupaOoaEVdVVAdozjS6bDQtXcKs0mxsivClXIaHLlMLpXIEADUBAAjBtfgQTHk3gRMhUUWkEV1OHhYPIADwDKB4GbyOTHU5nVzAPYHI4nc4mULACbDEzISygvAmAkAemJpLkoRSJhSIDyIG2fkMKHQ2FwjgAnlgEJYCAAFVRQQwGADyWDwsh6SIkTBYAHInpMALRYbW6-g2zjXG12bBodQwPn+NAOC2dfhaLp8vnurCe1rGgCyEH8CE57FID3YAhMhSjYD8CAoWp1+sNxrAyQl4F0iwAkuVlvxk2AUPQ6wgTEA

import * as React from "react";

export function Example({ things }) {
  const thing = things.find(thing => thing.something > 0);

  const [count, setCount] = React.useState(0);

  const label = thing.label.toLowerCase();
  
  const handleClick = React.useCallback(
    () => {
      setCount(count => count + 1);
    },
    [],
  );

  return <button onClick={handleClick}>{label}: {count}</button>;
};

Repro steps

  1. Create a local state using useState() hook
  2. Place it between two lines of code that contain chained method calls
  3. Use state setter inside useCallback()

Expected: state setter does not need to be mentioned in useCallback deps

Observed:

Found 1 error:

Compilation Skipped: Existing memoization could not be preserved

React Compiler has skipped optimizing this component because the existing manual memoization could
not be preserved. The inferred dependencies did not match the manually specified dependencies, which
could cause the value to change more or less frequently than expected. The inferred dependency was
`setCount`, but the source dependencies were []. Inferred dependency not present in source.

Interestingly, the bug only occurs if useState is 'sandwiched' between two other lines. These lines need to be related to each other and contain .method() calls. Just getting object properties (e.g. thing.label instead of thing.label.toLowerCase()) is not enough to trigger the bug.

How often does this bug happen?

Every time

What version of React are you using?

19.2.0+ (inside playground.react.dev)

What version of React Compiler are you using?

1.0.0+ (inside playground.react.dev)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bugType: Bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions