toString children of title (#25838)
children of title can either behave like children or like an attribute. We're kind of treating it more like an attribute now so we should support toString/valueOf like we do on attributes.
Sebastian Markbåge committed
Dec 7, 2022 at 09:41 UTC
bfcbf3306794eb3de37d866551127fc2bdae53d7
4 files changed
+53
-22
packages/react-dom-bindings/src/client/ReactDOMFloatClient.js
+16
-6
@@ -587,17 +587,27 @@ export function getResource(
587
return null;
588
}
589
case 'title': {
590
- let child = pendingProps.children;
591
- if (Array.isArray(child) && child.length === 1) {
592
- child = child[0];
590
+ const children = pendingProps.children;
591
+ let child;
592
+ if (Array.isArray(children)) {
593
+ child = children.length === 1 ? children[0] : null;
594
+ } else {
595
+ child = children;
596
}
594
- if (typeof child === 'string' || typeof child === 'number') {
597
+ if (
598
+ typeof child !== 'function' &&
599
+ typeof child !== 'symbol' &&
600
+ child !== null &&
601
+ child !== undefined
602
+ ) {
603
+ // eslint-disable-next-line react-internal/safe-string-coercion
604
+ const childString = '' + (child: any);
605
const headRoot: Document = getDocumentFromRoot(resourceRoot);
606
const headResources = getResourcesFromRoot(headRoot).head;
597
- const key = getTitleKey(child);
607
+ const key = getTitleKey(childString);
608
let resource = headResources.get(key);
609
if (!resource) {
600
- const titleProps = titlePropsFromRawProps(child, pendingProps);
610
+ const titleProps = titlePropsFromRawProps(childString, pendingProps);
611
resource = {
612
type: 'title',
613
props: titleProps,
packages/react-dom-bindings/src/server/ReactDOMFloatServer.js
+16
-6
@@ -652,17 +652,27 @@ export function resourcesFromElement(type: string, props: Props): boolean {
652
const resources = currentResources;
653
switch (type) {
654
case 'title': {
655
- let child = props.children;
656
- if (Array.isArray(child) && child.length === 1) {
657
- child = child[0];
655
+ const children = props.children;
656
+ let child;
657
+ if (Array.isArray(children)) {
658
+ child = children.length === 1 ? children[0] : null;
659
+ } else {
660
+ child = children;
661
}
659
- if (typeof child === 'string' || typeof child === 'number') {
660
- const key = 'title::' + child;
662
+ if (
663
+ typeof child !== 'function' &&
664
+ typeof child !== 'symbol' &&
665
+ child !== null &&
666
+ child !== undefined
667
+ ) {
668
+ // eslint-disable-next-line react-internal/safe-string-coercion
669
+ const childString = '' + (child: any);
670
+ const key = 'title::' + childString;
671
let resource = resources.headsMap.get(key);
672
if (!resource) {
673
resource = {
674
type: 'title',
665
- props: titlePropsFromRawProps(child, props),
675
+ props: titlePropsFromRawProps(childString, props),
676
flushed: false,
677
};
678
resources.headsMap.set(key, resource);
packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js
+13
-6
@@ -1473,12 +1473,19 @@ function pushTitleImpl(
1473
}
1474
target.push(endOfStartTag);
1475
1476
- const child =
1477
- Array.isArray(children) && children.length < 2
1478
- ? children[0] || null
1479
- : children;
1480
- if (typeof child === 'string' || typeof child === 'number') {
1481
- target.push(stringToChunk(escapeTextForBrowser(child)));
1476
+ const child = Array.isArray(children)
1477
+ ? children.length < 2
1478
+ ? children[0]
1479
+ : null
1480
+ : children;
1481
+ if (
1482
+ typeof child !== 'function' &&
1483
+ typeof child !== 'symbol' &&
1484
+ child !== null &&
1485
+ child !== undefined
1486
+ ) {
1487
+ // eslint-disable-next-line react-internal/safe-string-coercion
1488
+ target.push(stringToChunk(escapeTextForBrowser('' + child)));
1489
}
1490
target.push(endTag1, stringToChunk('title'), endTag2);
1491
return null;
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+8
-4
@@ -5144,8 +5144,10 @@ describe('ReactDOMFizzServer', () => {
5144
}
5145
5146
if (gate(flags => flags.enableFloat)) {
5147
- // invalid titles are not emitted on the server when float is on
5148
- expect(getVisibleChildren(container)).toEqual(undefined);
5147
+ // object titles are toStringed when float is on
5148
+ expect(getVisibleChildren(container)).toEqual(
5149
+ <title>{'[object Object]'}</title>,
5150
+ );
5151
} else {
5152
expect(getVisibleChildren(container)).toEqual(<title>hello</title>);
5153
}
@@ -5159,8 +5161,10 @@ describe('ReactDOMFizzServer', () => {
5161
expect(Scheduler).toFlushAndYield([]);
5162
expect(errors).toEqual([]);
5163
if (gate(flags => flags.enableFloat)) {
5162
- // invalid titles are not emitted on the server when float is on
5163
- expect(getVisibleChildren(container)).toEqual(undefined);
5164
+ // object titles are toStringed when float is on
5165
+ expect(getVisibleChildren(container)).toEqual(
5166
+ <title>{'[object Object]'}</title>,
5167
+ );
5168
} else {
5169
expect(getVisibleChildren(container)).toEqual(<title>hello</title>);
5170
}