From 16367ceb02faf5673e0380dec4c4928dfa37f17b Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Tue, 10 Dec 2024 16:11:17 -0500 Subject: [PATCH] [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 ``` The bugged output is: ```javascript { // ... props: props } ``` With this change we now get the correct output: ```javascript { // ... props: {ref: ref, ...props} } ``` --- .../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 = []; - 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..01b1470f93c8b 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 ; +} + // TODO: Support value blocks function TernaryJsx({cond}) { return cond ?
: 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 = ; + } 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 ; +} + // TODO: Support value blocks function TernaryJsx({cond}) { return cond ?
: null;