[compiler] Fix dropped ref with spread props in InlineJsxTransform (#31726)
When supporting ref as prop in https://github.com/facebook/react/pull/31558, I missed fixing the optimization to pass a spread-props-only props object in without an additional object copy. In the case that we have only a ref along with a spread, we cannot return only the spread object. This results in dropping the ref. In this example ```javascript <Foo ref={ref} {...props} /> ``` The bugged output is: ```javascript { // ... props: props } ``` With this change we now get the correct output: ```javascript { // ... props: {ref: ref, ...props} } ```
Jack Pope committed
Dec 10, 2024 at 16:11 UTC
16367ceb02faf5673e0380dec4c4928dfa37f17b
3 files changed
+46
-5
compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts
+3
-5
@@ -546,16 +546,14 @@ function createPropsProperties(
546
let refProperty: ObjectProperty | undefined;
547
let keyProperty: ObjectProperty | undefined;
548
const props: Array<ObjectProperty | SpreadPattern> = [];
549
- const jsxAttributesWithoutKeyAndRef = propAttributes.filter(
550
- p => p.kind === 'JsxAttribute' && p.name !== 'key' && p.name !== 'ref',
549
+ const jsxAttributesWithoutKey = propAttributes.filter(
550
+ p => p.kind === 'JsxAttribute' && p.name !== 'key',
551
);
552
const jsxSpreadAttributes = propAttributes.filter(
553
p => p.kind === 'JsxSpreadAttribute',
554
);
555
const spreadPropsOnly =
556
- jsxAttributesWithoutKeyAndRef.length === 0 &&
557
- jsxSpreadAttributes.length === 1;
558
-
556
+ jsxAttributesWithoutKey.length === 0 && jsxSpreadAttributes.length === 1;
557
propAttributes.forEach(prop => {
558
switch (prop.kind) {
559
case 'JsxAttribute': {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.expect.md
+39
@@ -60,6 +60,10 @@ function ConditionalJsx({shouldWrap}) {
60
return content;
61
}
62
63
+function ComponentWithSpreadPropsAndRef({ref, ...other}) {
64
+ return <Foo ref={ref} {...other} />;
65
+}
66
+
67
// TODO: Support value blocks
68
function TernaryJsx({cond}) {
69
return cond ? <div /> : null;
@@ -409,6 +413,41 @@ function ConditionalJsx(t0) {
413
return content;
414
}
415
416
+function ComponentWithSpreadPropsAndRef(t0) {
417
+ const $ = _c2(6);
418
+ let other;
419
+ let ref;
420
+ if ($[0] !== t0) {
421
+ ({ ref, ...other } = t0);
422
+ $[0] = t0;
423
+ $[1] = other;
424
+ $[2] = ref;
425
+ } else {
426
+ other = $[1];
427
+ ref = $[2];
428
+ }
429
+ let t1;
430
+ if ($[3] !== other || $[4] !== ref) {
431
+ if (DEV) {
432
+ t1 = <Foo ref={ref} {...other} />;
433
+ } else {
434
+ t1 = {
435
+ $$typeof: Symbol.for("react.transitional.element"),
436
+ type: Foo,
437
+ ref: ref,
438
+ key: null,
439
+ props: { ref: ref, ...other },
440
+ };
441
+ }
442
+ $[3] = other;
443
+ $[4] = ref;
444
+ $[5] = t1;
445
+ } else {
446
+ t1 = $[5];
447
+ }
448
+ return t1;
449
+}
450
+
451
// TODO: Support value blocks
452
function TernaryJsx(t0) {
453
const $ = _c2(2);
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.js
+4
@@ -56,6 +56,10 @@ function ConditionalJsx({shouldWrap}) {
56
return content;
57
}
58
59
+function ComponentWithSpreadPropsAndRef({ref, ...other}) {
60
+ return <Foo ref={ref} {...other} />;
61
+}
62
+
63
// TODO: Support value blocks
64
function TernaryJsx({cond}) {
65
return cond ? <div /> : null;