Skip to content

Commit 322d5f9

Browse files
committed
[compiler] treat ref-like identifiers as refs by default
`@enableTreatRefLikeIdentifiersAsRefs` is now on by default. I made one small fix to the render helper logic as part of this, uncovered by including more tests.
1 parent 62b9892 commit 322d5f9

22 files changed

+200
-149
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ export const EnvironmentConfigSchema = z.object({
608608
*
609609
* Here the variables `ref` and `myRef` will be typed as Refs.
610610
*/
611-
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(false),
611+
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(true),
612612

613613
/*
614614
* If specified a value, the compiler lowers any calls to `useContext` to use

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,7 @@ function validateNoRefAccessInRenderImpl(
432432
* By default we check that function call operands are not refs,
433433
* ref values, or functions that can access refs.
434434
*/
435-
if (
436-
isRefLValue ||
437-
interpolatedAsJsx.has(instr.lvalue.identifier.id) ||
438-
hookKind != null
439-
) {
435+
if (isRefLValue || hookKind != null) {
440436
/**
441437
* Special cases:
442438
*
@@ -452,18 +448,21 @@ function validateNoRefAccessInRenderImpl(
452448
*
453449
* Eg `const mergedRef = mergeRefs(ref1, ref2)`
454450
*
455-
* 2) the lvalue is passed as a jsx child
456-
*
457-
* For example `<Foo>{renderHelper(ref)}</Foo>`. Here we have more
458-
* context and infer that the ref is being passed to a component-like
459-
* render function which attempts to obey the rules.
460-
*
461451
* 3) hooks
462452
*
463453
* Hooks are independently checked to ensure they don't access refs
464454
* during render.
465455
*/
466456
validateNoDirectRefValueAccess(errors, operand, env);
457+
} else if (interpolatedAsJsx.has(instr.lvalue.identifier.id)) {
458+
/**
459+
* 2) the lvalue is passed as a jsx child
460+
*
461+
* For example `<Foo>{renderHelper(ref)}</Foo>`. Here we have more
462+
* context and infer that the ref is being passed to a component-like
463+
* render function which attempts to obey the rules.
464+
*/
465+
validateNoRefValueAccess(errors, env, operand);
467466
} else {
468467
validateNoRefPassedToFunction(
469468
errors,

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-effect-indirect.expect.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function Component() {
2727
}
2828

2929
function Child({ref}) {
30+
'use no memo';
3031
// This violates the rules of React, so we access the ref in a child
3132
// component
3233
return ref.current;
@@ -100,8 +101,10 @@ function Component() {
100101
return t6;
101102
}
102103

103-
function Child(t0) {
104-
const { ref } = t0;
104+
function Child({ ref }) {
105+
"use no memo";
106+
// This violates the rules of React, so we access the ref in a child
107+
// component
105108
return ref.current;
106109
}
107110

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-effect-indirect.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function Component() {
2323
}
2424

2525
function Child({ref}) {
26+
'use no memo';
2627
// This violates the rules of React, so we access the ref in a child
2728
// component
2829
return ref.current;

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-effect.expect.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function Component() {
2323
}
2424

2525
function Child({ref}) {
26+
'use no memo';
2627
// This violates the rules of React, so we access the ref in a child
2728
// component
2829
return ref.current;
@@ -86,8 +87,10 @@ function Component() {
8687
return t5;
8788
}
8889

89-
function Child(t0) {
90-
const { ref } = t0;
90+
function Child({ ref }) {
91+
"use no memo";
92+
// This violates the rules of React, so we access the ref in a child
93+
// component
9194
return ref.current;
9295
}
9396

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-effect.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ function Component() {
1919
}
2020

2121
function Child({ref}) {
22+
'use no memo';
2223
// This violates the rules of React, so we access the ref in a child
2324
// component
2425
return ref.current;

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.expect.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function Component() {
2525
}
2626

2727
function Child({ref}) {
28+
'use no memo';
2829
// This violates the rules of React, so we access the ref in a child
2930
// component
3031
return ref.current;
@@ -83,8 +84,10 @@ function Component() {
8384
}
8485
function _temp() {}
8586

86-
function Child(t0) {
87-
const { ref } = t0;
87+
function Child({ ref }) {
88+
"use no memo";
89+
// This violates the rules of React, so we access the ref in a child
90+
// component
8891
return ref.current;
8992
}
9093

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/allow-ref-access-in-unused-callback-nested.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function Component() {
2121
}
2222

2323
function Child({ref}) {
24+
'use no memo';
2425
// This violates the rules of React, so we access the ref in a child
2526
// component
2627
return ref.current;

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-aliased-ref-in-callback-invoked-during-render-.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ error.invalid-aliased-ref-in-callback-invoked-during-render-.ts:9:33
2929
7 | return <Foo item={item} current={current} />;
3030
8 | };
3131
> 9 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
32-
| ^^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
3333
10 | }
3434
11 |
3535
```

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-callback-invoked-during-render.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ error.invalid-ref-in-callback-invoked-during-render.ts:8:33
2828
6 | return <Foo item={item} current={current} />;
2929
7 | };
3030
> 8 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
31-
| ^^^^^^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
3232
9 | }
3333
10 |
3434
```

0 commit comments

Comments
 (0)