Skip to content

[Bug]: React Compiler transforms postfix increment into non-equivalent code and changes runtime semantics #15256

Description

@jianhuagao

System Info

System:
  OS: Windows 10 10.0.19045
  CPU: (4) x64 Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz
  Memory: 13.06 GB / 31.88 GB

Binaries:
  Node: 24.12.0 - C:\Program Files\nodejs\node.EXE
  Yarn: 1.22.19 - C:\Users\WHAdmin\AppData\Roaming\npm\yarn.CMD
  npm: 10.2.1 - C:\Users\WHAdmin\AppData\Roaming\npm\npm.CMD
  pnpm: 11.21.0 - C:\Users\WHAdmin\AppData\Roaming\npm\pnpm.CMD

Browsers:
  Chrome: 151.0.7922.137
  Edge: Chromium (151.0.4129.93)
  Internet Explorer: 11.0.19041.5794

Relevant package versions:

{
  "react": "^19.2.8",
  "react-dom": "^19.2.8",
  "@rspack/core": "^2.1.10"
}

Details

When using React Compiler with Rspack, the following expression is incorrectly transformed.

Source code:

if (retainRef.current++ <= 0) {
  // ...
}

The generated code becomes effectively:

retainRef.current = retainRef.current + 1;

if (retainRef.current <= 0) {
  // ...
}

These two expressions are not semantically equivalent because x++ is a postfix increment expression: the comparison must use the value before the increment.

For example, if:

retainRef.current = 0;

the original code behaves as follows:

if (retainRef.current++ <= 0) {
  // This branch IS executed
}

// retainRef.current === 1

The comparison evaluates:

0 <= 0 // true

and only then increments retainRef.current to 1.

However, after the transformation:

retainRef.current = retainRef.current + 1;

if (retainRef.current <= 0) {
  // This branch is NOT executed
}

the comparison becomes:

1 <= 0 // false

Therefore, this transformation changes the runtime behavior of the program.

Conceptually:

x++ <= 0

may be transformed into something equivalent to:

const previous = x;
x = x + 1;

previous <= 0;

but it must not be transformed into:

x = x + 1;

x <= 0;

This issue can cause subtle runtime bugs because the generated code is still valid JavaScript and does not produce any compilation or runtime error. It simply executes different logic from the source code.

Reproduce link

No response

Reproduce Steps

A minimal example:

import { useCallback, useRef } from 'react';

export function App() {
  const retainRef = useRef(0);

  const test = useCallback(() => {
    if (retainRef.current++ <= 0) {
      console.log('condition matched');
    }

    console.log('current:', retainRef.current);
  }, []);

  return <button onClick={test}>Test</button>;
}
  1. Build/compile the code with Rspack and React Compiler enabled.
  2. Inspect the generated JavaScript.
  3. The original expression:
if (retainRef.current++ <= 0)

is transformed into code equivalent to:

retainRef.current = retainRef.current + 1;

if (retainRef.current <= 0)
  1. Run the code with retainRef.current initially set to 0.

Expected output:

condition matched
current: 1

Actual output after the transformation:

current: 1

The condition is no longer entered because the comparison incorrectly uses the incremented value.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions