Skip to content

[Compiler Bug]: React Compiler changes quoted computed property access to dot notation #37231

Description

@imjordanxd

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-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/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYhBABQCURwAOsUXIWDkQEYCGMRAvEVDAIAQt2oBtAORcAXpIC6AbgZEiMBDljEAPAD4AEggA2RiABpaXGAF9tAel3LM1kGZDNMaPAHMUIeox2dswAtgAOeEac+IQAshAAJgjIRHQgnCZpDC5uYAAWEADuAJKYOAgwmBlgKDgwUAjWQA

Repro steps

React Compiler changes a quoted computed property access such as object['property'] into object.property. Although these expressions are normally equivalent in JavaScript, they are not equivalent when the output is subsequently processed by Closure Compiler. Closure Compiler treats quoted property accesses as non-renamable, while dot-property accesses may be renamed.

For example:

function Foo() {
  const namespace = useParams()['namespace'];
  return <>Hello, {namespace}</>;
}

is compiled to:

import { c as _c } from "react/compiler-runtime";

function Foo() {
  const $ = _c(2);
  const namespace = useParams().namespace;
  let t0;

  if ($[0] !== namespace) {
    t0 = <>Hello, {namespace}</>;
    $[0] = namespace;
    $[1] = t0;
  } else {
    t0 = $[1];
  }

  return t0;
}

In our application, useParams() returns an object whose keys come from React Router’s route parameter strings. Closure Compiler cannot rename those externally produced keys. The authored bracket notation is therefore intentional:

useParams()['namespace'];

After React Compiler changes it to:

useParams().namespace;

Closure may rename namespace, causing the application to read a property that React Router never sets. More generally, React Compiler should avoid rewriting JavaScript syntax when that rewrite is not necessary for a React-specific optimisation. Although object['property'] and object.property usually have the same runtime semantics, other tools in the compilation pipeline may intentionally distinguish between them. Since changing the property-access syntax provides no benefit to React’s memoization output, the compiler should preserve the developer-authored form while still representing the property as statically known internally.

Expected behaviour
React Compiler should preserve the quoted computed-access syntax:

const namespace = useParams()['namespace'];

It can still memoize the resulting value and treat "namespace" as a statically known property internally.

Actual behaviour
The quoted access is emitted as dot notation:

const namespace = useParams().namespace;

Additional context
This is not specific to useParams. Quoted property access is commonly used to identify externally defined or non-renamable property names when compiling with Closure Compiler. The HIR initially represents this syntax as a ComputedLoad. Constant propagation recognises that the key is a constant string and converts it to a PropertyLoad. However, PropertyLoad does not retain whether the original access used bracket notation, and code generation emits string-valued PropertyLoads using dot notation. One possible solution would be to preserve a computed or equivalent syntax flag when promoting a ComputedLoad to a static PropertyLoad. That would allow the compiler to retain its existing analysis and optimisation behaviour while emitting:

object['property']

instead of:

object.property

How often does this bug happen?

Every time

What version of React are you using?

19.2.3

What version of React Compiler are you using?

1.0.0

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