[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}
}
```
This commit is contained in:
Jack Pope
2024-12-10 16:11:17 -05:00
committed by GitHub
parent 7cb356e862
commit 16367ceb02
3 changed files with 46 additions and 5 deletions
@@ -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': {
@@ -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);
@@ -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;