From 20ae5d31d07990e2055d8e9250a94a24636083a3 Mon Sep 17 00:00:00 2001
From: Jack Pope <jackpope@meta.com>
Date: Tue, 10 Dec 2024 15:14:36 -0500
Subject: [PATCH] [compiler] Fix dropped ref with spread props in
 InlineJsxTransform

---
 .../src/Optimization/InlineJsxTransform.ts    |  8 ++--
 .../compiler/inline-jsx-transform.expect.md   | 39 +++++++++++++++++++
 .../fixtures/compiler/inline-jsx-transform.js |  4 ++
 3 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts
index 2851ed7ee39a1..9ffc64864f397 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/Optimization/InlineJsxTransform.ts
@@ -546,16 +546,14 @@ function createPropsProperties(
   let refProperty: ObjectProperty | undefined;
   let keyProperty: ObjectProperty | undefined;
   const props: Array<ObjectProperty | SpreadPattern> = [];
-  const jsxAttributesWithoutKeyAndRef = propAttributes.filter(
-    p => p.kind === 'JsxAttribute' && p.name !== 'key' && p.name !== 'ref',
+  const jsxAttributesWithoutKey = propAttributes.filter(
+    p => p.kind === 'JsxAttribute' && p.name !== 'key',
   );
   const jsxSpreadAttributes = propAttributes.filter(
     p => p.kind === 'JsxSpreadAttribute',
   );
   const spreadPropsOnly =
-    jsxAttributesWithoutKeyAndRef.length === 0 &&
-    jsxSpreadAttributes.length === 1;
-
+    jsxAttributesWithoutKey.length === 0 && jsxSpreadAttributes.length === 1;
   propAttributes.forEach(prop => {
     switch (prop.kind) {
       case 'JsxAttribute': {
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.expect.md
index 91bd0ad0b750e..29e188327d24c 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.expect.md
@@ -60,6 +60,10 @@ function ConditionalJsx({shouldWrap}) {
   return content;
 }
 
+function ComponentWithSpreadPropsAndRef({ref, ...other}) {
+  return <Foo ref={ref} {...other} />
+}
+
 // TODO: Support value blocks
 function TernaryJsx({cond}) {
   return cond ? <div /> : null;
@@ -409,6 +413,41 @@ function ConditionalJsx(t0) {
   return content;
 }
 
+function ComponentWithSpreadPropsAndRef(t0) {
+  const $ = _c2(6);
+  let other;
+  let ref;
+  if ($[0] !== t0) {
+    ({ ref, ...other } = t0);
+    $[0] = t0;
+    $[1] = other;
+    $[2] = ref;
+  } else {
+    other = $[1];
+    ref = $[2];
+  }
+  let t1;
+  if ($[3] !== other || $[4] !== ref) {
+    if (DEV) {
+      t1 = <Foo ref={ref} {...other} />;
+    } else {
+      t1 = {
+        $$typeof: Symbol.for("react.transitional.element"),
+        type: Foo,
+        ref: ref,
+        key: null,
+        props: { ref: ref, ...other },
+      };
+    }
+    $[3] = other;
+    $[4] = ref;
+    $[5] = t1;
+  } else {
+    t1 = $[5];
+  }
+  return t1;
+}
+
 // TODO: Support value blocks
 function TernaryJsx(t0) {
   const $ = _c2(2);
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.js
index ca55cab4ff60a..2ab1efef794b6 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.js
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/inline-jsx-transform.js
@@ -56,6 +56,10 @@ function ConditionalJsx({shouldWrap}) {
   return content;
 }
 
+function ComponentWithSpreadPropsAndRef({ref, ...other}) {
+  return <Foo ref={ref} {...other} />;
+}
+
 // TODO: Support value blocks
 function TernaryJsx({cond}) {
   return cond ? <div /> : null;