@samitouri / QOS-React / commits / 16367ceb02

[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;