@samitouri / QOS-React-2 / commits / bfcbf33067

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 }