Skip to content

Commit 2f240c9

Browse files
authored
Add support for rendering BigInt (#24580)
1 parent 6c3b8db commit 2f240c9

30 files changed

+389
-36
lines changed

packages/react-devtools-shell/src/app/InspectableElements/UnserializableProps.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,9 @@ export default function UnserializableProps(): React.Node {
5858
}
5959

6060
function ChildComponent(props: any) {
61-
return null;
61+
return (
62+
<>
63+
<div>{props.bigInt}</div>
64+
</>
65+
);
6266
}

packages/react-dom-bindings/src/client/ReactDOMComponent.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import {validateProperties as validateUnknownProperties} from '../shared/ReactDO
6666
import sanitizeURL from '../shared/sanitizeURL';
6767

6868
import {
69+
enableBigIntSupport,
6970
enableCustomElementPropertySupport,
7071
enableClientRenderFallbackOnTextMismatch,
7172
enableFormActions,
@@ -326,7 +327,7 @@ function normalizeMarkupForTextOrAttribute(markup: mixed): string {
326327

327328
export function checkForUnmatchedText(
328329
serverText: string,
329-
clientText: string | number,
330+
clientText: string | number | bigint,
330331
isConcurrentMode: boolean,
331332
shouldWarnDev: boolean,
332333
) {
@@ -397,12 +398,17 @@ function setProp(
397398
if (canSetTextContent) {
398399
setTextContent(domElement, value);
399400
}
400-
} else if (typeof value === 'number') {
401+
} else if (
402+
typeof value === 'number' ||
403+
(enableBigIntSupport && typeof value === 'bigint')
404+
) {
401405
if (__DEV__) {
406+
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
402407
validateTextNesting('' + value, tag);
403408
}
404409
const canSetTextContent = tag !== 'body';
405410
if (canSetTextContent) {
411+
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
406412
setTextContent(domElement, '' + value);
407413
}
408414
}
@@ -955,7 +961,11 @@ function setPropOnCustomElement(
955961
case 'children': {
956962
if (typeof value === 'string') {
957963
setTextContent(domElement, value);
958-
} else if (typeof value === 'number') {
964+
} else if (
965+
typeof value === 'number' ||
966+
(enableBigIntSupport && typeof value === 'bigint')
967+
) {
968+
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
959969
setTextContent(domElement, '' + value);
960970
}
961971
break;
@@ -2817,7 +2827,12 @@ export function diffHydratedProperties(
28172827
// even listeners these nodes might be wired up to.
28182828
// TODO: Warn if there is more than a single textNode as a child.
28192829
// TODO: Should we use domElement.firstChild.nodeValue to compare?
2820-
if (typeof children === 'string' || typeof children === 'number') {
2830+
if (
2831+
typeof children === 'string' ||
2832+
typeof children === 'number' ||
2833+
(enableBigIntSupport && typeof children === 'bigint')
2834+
) {
2835+
// $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint
28212836
if (domElement.textContent !== '' + children) {
28222837
if (props.suppressHydrationWarning !== true) {
28232838
checkForUnmatchedText(

packages/react-dom-bindings/src/client/ReactDOMOption.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import {Children} from 'react';
11+
import {enableBigIntSupport} from 'shared/ReactFeatureFlags';
1112

1213
let didWarnSelectedSetOnOption = false;
1314
let didWarnInvalidChild = false;
@@ -26,7 +27,11 @@ export function validateOptionProps(element: Element, props: Object) {
2627
if (child == null) {
2728
return;
2829
}
29-
if (typeof child === 'string' || typeof child === 'number') {
30+
if (
31+
typeof child === 'string' ||
32+
typeof child === 'number' ||
33+
(enableBigIntSupport && typeof child === 'bigint')
34+
) {
3035
return;
3136
}
3237
if (!didWarnInvalidChild) {

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ import {
8989
import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';
9090

9191
import {
92+
enableBigIntSupport,
9293
enableCreateEventHandleAPI,
9394
enableScopeAPI,
9495
enableFloat,
@@ -548,6 +549,7 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
548549
type === 'noscript' ||
549550
typeof props.children === 'string' ||
550551
typeof props.children === 'number' ||
552+
(enableBigIntSupport && typeof props.children === 'bigint') ||
551553
(typeof props.dangerouslySetInnerHTML === 'object' &&
552554
props.dangerouslySetInnerHTML !== null &&
553555
props.dangerouslySetInnerHTML.__html != null)

packages/react-dom-bindings/src/client/ToStringValue.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
*/
99

1010
import {checkFormFieldValueStringCoercion} from 'shared/CheckStringCoercion';
11+
import {enableBigIntSupport} from 'shared/ReactFeatureFlags';
1112

1213
export opaque type ToStringValue =
1314
| boolean
1415
| number
16+
| bigint
1517
| Object
1618
| string
1719
| null
@@ -28,6 +30,12 @@ export function toString(value: ToStringValue): string {
2830

2931
export function getToStringValue(value: mixed): ToStringValue {
3032
switch (typeof value) {
33+
case 'bigint':
34+
if (!enableBigIntSupport) {
35+
// bigint is assigned as empty string
36+
return '';
37+
}
38+
// fallthrough for BigInt support
3139
case 'boolean':
3240
case 'number':
3341
case 'string':

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
import {Children} from 'react';
2929

3030
import {
31+
enableBigIntSupport,
3132
enableFilterEmptyStringAttributesDOM,
3233
enableCustomElementPropertySupport,
3334
enableFloat,
@@ -1626,7 +1627,9 @@ function flattenOptionChildren(children: mixed): string {
16261627
if (
16271628
!didWarnInvalidOptionChildren &&
16281629
typeof child !== 'string' &&
1629-
typeof child !== 'number'
1630+
typeof child !== 'number' &&
1631+
((enableBigIntSupport && typeof child !== 'bigint') ||
1632+
!enableBigIntSupport)
16301633
) {
16311634
didWarnInvalidOptionChildren = true;
16321635
console.error(
@@ -2960,36 +2963,40 @@ function pushTitle(
29602963

29612964
if (Array.isArray(children) && children.length > 1) {
29622965
console.error(
2963-
'React expects the `children` prop of <title> tags to be a string, number, or object with a novel `toString` method but found an Array with length %s instead.' +
2966+
'React expects the `children` prop of <title> tags to be a string, number%s, or object with a novel `toString` method but found an Array with length %s instead.' +
29642967
' Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert `children` of <title> tags to a single string value' +
29652968
' which is why Arrays of length greater than 1 are not supported. When using JSX it can be commong to combine text nodes and value nodes.' +
29662969
' For example: <title>hello {nameOfUser}</title>. While not immediately apparent, `children` in this case is an Array with length 2. If your `children` prop' +
29672970
' is using this form try rewriting it using a template string: <title>{`hello ${nameOfUser}`}</title>.',
2971+
enableBigIntSupport ? ', bigint' : '',
29682972
children.length,
29692973
);
29702974
} else if (typeof child === 'function' || typeof child === 'symbol') {
29712975
const childType =
29722976
typeof child === 'function' ? 'a Function' : 'a Sybmol';
29732977
console.error(
2974-
'React expect children of <title> tags to be a string, number, or object with a novel `toString` method but found %s instead.' +
2978+
'React expect children of <title> tags to be a string, number%s, or object with a novel `toString` method but found %s instead.' +
29752979
' Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title>' +
29762980
' tags to a single string value.',
2981+
enableBigIntSupport ? ', bigint' : '',
29772982
childType,
29782983
);
29792984
} else if (child && child.toString === {}.toString) {
29802985
if (child.$$typeof != null) {
29812986
console.error(
2982-
'React expects the `children` prop of <title> tags to be a string, number, or object with a novel `toString` method but found an object that appears to be' +
2987+
'React expects the `children` prop of <title> tags to be a string, number%s, or object with a novel `toString` method but found an object that appears to be' +
29832988
' a React element which never implements a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to' +
29842989
' be able to convert children of <title> tags to a single string value which is why rendering React elements is not supported. If the `children` of <title> is' +
29852990
' a React Component try moving the <title> tag into that component. If the `children` of <title> is some HTML markup change it to be Text only to be valid HTML.',
2991+
enableBigIntSupport ? ', bigint' : '',
29862992
);
29872993
} else {
29882994
console.error(
2989-
'React expects the `children` prop of <title> tags to be a string, number, or object with a novel `toString` method but found an object that does not implement' +
2995+
'React expects the `children` prop of <title> tags to be a string, number%s, or object with a novel `toString` method but found an object that does not implement' +
29902996
' a suitable `toString` method. Browsers treat all child Nodes of <title> tags as Text content and React expects to be able to convert children of <title> tags' +
29912997
' to a single string value. Using the default `toString` method available on every object is almost certainly an error. Consider whether the `children` of this <title>' +
29922998
' is an object in error and change it to a string or number value if so. Otherwise implement a `toString` method that React can use to produce a valid <title>.',
2999+
enableBigIntSupport ? ', bigint' : '',
29933000
);
29943001
}
29953002
}
@@ -3123,14 +3130,17 @@ function pushStartTitle(
31233130
} else if (
31243131
childForValidation != null &&
31253132
typeof childForValidation !== 'string' &&
3126-
typeof childForValidation !== 'number'
3133+
typeof childForValidation !== 'number' &&
3134+
((enableBigIntSupport && typeof childForValidation !== 'bigint') ||
3135+
!enableBigIntSupport)
31273136
) {
31283137
console.error(
3129-
'A title element received a value that was not a string or number for children. ' +
3138+
'A title element received a value that was not a string or number%s for children. ' +
31303139
'In the browser title Elements can only have Text Nodes as children. If ' +
31313140
'the children being rendered output more than a single text node in aggregate the browser ' +
31323141
'will display markup and comments as text in the title and hydration will likely fail and ' +
31333142
'fall back to client rendering',
3143+
enableBigIntSupport ? ' or bigint' : '',
31343144
);
31353145
}
31363146
}

packages/react-dom-bindings/src/server/escapeTextForBrowser.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
*/
4040

4141
import {checkHtmlStringCoercion} from 'shared/CheckStringCoercion';
42+
import {enableBigIntSupport} from 'shared/ReactFeatureFlags';
4243

4344
const matchHtmlRegExp = /["'&<>]/;
4445

@@ -106,7 +107,11 @@ function escapeHtml(string: string) {
106107
* @return {string} An escaped string.
107108
*/
108109
function escapeTextForBrowser(text: string | number | boolean): string {
109-
if (typeof text === 'boolean' || typeof text === 'number') {
110+
if (
111+
typeof text === 'boolean' ||
112+
typeof text === 'number' ||
113+
(enableBigIntSupport && typeof text === 'bigint')
114+
) {
110115
// this shortcircuit helps perf for types that we know will never have
111116
// special characters, especially given that this function is used often
112117
// for numeric dom ids.

packages/react-dom/src/__tests__/ReactDOMFiber-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ describe('ReactDOMFiber', () => {
6060
expect(container.textContent).toEqual('10');
6161
});
6262

63+
// @gate enableBigIntSupport
64+
it('should render bigints as children', async () => {
65+
const Box = ({value}) => <div>{value}</div>;
66+
67+
await act(async () => {
68+
root.render(<Box value={10n} />);
69+
});
70+
71+
expect(container.textContent).toEqual('10');
72+
});
73+
6374
it('should call an effect after mount/update (replacing render callback pattern)', async () => {
6475
function Component() {
6576
React.useEffect(() => {

0 commit comments

Comments
 (0)