diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts index 7da564205475..063b919e288b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts @@ -156,6 +156,20 @@ function collectTemporariesSidemap(fn: HIRFunction, env: Env): void { ) { continue; } + /* + * Skip the temporary alias when the loaded property is itself a ref + * (e.g. `props.ref`). The loaded value is a new ref value with its + * own env classification; widening env[object] through the alias + * would incorrectly make the object appear to be a ref and cause + * false positive ref-access errors on unrelated sibling property + * reads. + */ + if ( + isUseRefType(lvalue.identifier) || + isRefValueType(lvalue.identifier) + ) { + break; + } const temp = env.lookup(value.object); if (temp != null) { env.define(lvalue, temp); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.expect.md new file mode 100644 index 000000000000..672b76e98aeb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34342 + * Accessing both `props.ref` and a non-ref sibling property (here + * `props.value`) on the same props object should not raise a ref + * validation error on the sibling read. + */ +function Component(props) { + return
{props.value}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 'hello'}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34342 + * Accessing both `props.ref` and a non-ref sibling property (here + * `props.value`) on the same props object should not raise a ref + * validation error on the sibling read. + */ +function Component(props) { + const $ = _c(3); + let t0; + if ($[0] !== props.ref || $[1] !== props.value) { + t0 =
{props.value}
; + $[0] = props.ref; + $[1] = props.value; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ value: "hello" }], +}; + +``` + +### Eval output +(kind: ok)
hello
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.js new file mode 100644 index 000000000000..c8632976e3ab --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-accessing-non-ref-prop-alongside-props-ref.js @@ -0,0 +1,16 @@ +// @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34342 + * Accessing both `props.ref` and a non-ref sibling property (here + * `props.value`) on the same props object should not raise a ref + * validation error on the sibling read. + */ +function Component(props) { + return
{props.value}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{value: 'hello'}], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.expect.md new file mode 100644 index 000000000000..b006ff3c4376 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.expect.md @@ -0,0 +1,72 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34775 + * Forwarding `props.ref` to a child component should not cause sibling + * property reads from the same `props` object to be flagged as ref accesses. + */ +function Field(props) { + return ( + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Field, + params: [{placeholder: 'hello', disabled: false}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34775 + * Forwarding `props.ref` to a child component should not cause sibling + * property reads from the same `props` object to be flagged as ref accesses. + */ +function Field(props) { + const $ = _c(4); + let t0; + if ( + $[0] !== props.disabled || + $[1] !== props.placeholder || + $[2] !== props.ref + ) { + t0 = ( + + ); + $[0] = props.disabled; + $[1] = props.placeholder; + $[2] = props.ref; + $[3] = t0; + } else { + t0 = $[3]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Field, + params: [{ placeholder: "hello", disabled: false }], +}; + +``` + +### Eval output +(kind: exception) Control is not defined \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.js new file mode 100644 index 000000000000..87e2d5b383cf --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-forwarding-props-ref-does-not-taint-sibling-props.js @@ -0,0 +1,21 @@ +// @validateRefAccessDuringRender + +/** + * Regression test for https://github.com/facebook/react/issues/34775 + * Forwarding `props.ref` to a child component should not cause sibling + * property reads from the same `props` object to be flagged as ref accesses. + */ +function Field(props) { + return ( + + ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Field, + params: [{placeholder: 'hello', disabled: false}], +};