forked from facebook/react
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCheckStringCoercion.js
More file actions
155 lines (145 loc) · 5.44 KB
/
CheckStringCoercion.js
File metadata and controls
155 lines (145 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
/*
* The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol
* and Temporal.* types. See https://github.com/facebook/react/pull/22064.
*
* The functions in this module will throw an easier-to-understand,
* easier-to-debug exception with a clear errors message message explaining the
* problem. (Instead of a confusing exception thrown inside the implementation
* of the `value` object).
*/
// $FlowFixMe only called in DEV, so void return is not possible.
function typeName(value: mixed): string {
if (__DEV__) {
// toStringTag is needed for namespaced types like Temporal.Instant
const hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;
const type =
(hasToStringTag && (value: any)[Symbol.toStringTag]) ||
(value: any).constructor.name ||
'Object';
return type;
}
}
// $FlowFixMe only called in DEV, so void return is not possible.
function willCoercionThrow(value: mixed): boolean {
if (__DEV__) {
try {
testStringCoercion(value);
return false;
} catch (e) {
return true;
}
}
}
function testStringCoercion(value: mixed) {
// If you ended up here by following an exception call stack, here's what's
// happened: you supplied an object or symbol value to React (as a prop, key,
// DOM attribute, CSS property, string ref, etc.) and when React tried to
// coerce it to a string using `'' + value`, an exception was thrown.
//
// The most common types that will cause this exception are `Symbol` instances
// and Temporal objects like `Temporal.Instant`. But any object that has a
// `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this
// exception. (Library authors do this to prevent users from using built-in
// numeric operators like `+` or comparison operators like `>=` because custom
// methods are needed to perform accurate arithmetic or comparison.)
//
// To fix the problem, coerce this object or symbol value to a string before
// passing it to React. The most reliable way is usually `String(value)`.
//
// To find which value is throwing, check the browser or debugger console.
// Before this exception was thrown, there should be `console.error` output
// that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the
// problem and how that type was used: key, atrribute, input value prop, etc.
// In most cases, this console output also shows the component and its
// ancestor components where the exception happened.
//
// eslint-disable-next-line react-internal/safe-string-coercion
return '' + (value: any);
}
export function checkAttributeStringCoercion(
value: mixed,
attributeName: string,
) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'The provided `%s` attribute is an unsupported type %s.' +
' This value must be coerced to a string before before using it here.',
attributeName,
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
export function checkKeyStringCoercion(value: mixed) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'The provided key is an unsupported type %s.' +
' This value must be coerced to a string before before using it here.',
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
export function checkPropStringCoercion(value: mixed, propName: string) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'The provided `%s` prop is an unsupported type %s.' +
' This value must be coerced to a string before before using it here.',
propName,
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
export function checkCSSPropertyStringCoercion(value: mixed, propName: string) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'The provided `%s` CSS property is an unsupported type %s.' +
' This value must be coerced to a string before before using it here.',
propName,
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
export function checkHtmlStringCoercion(value: mixed) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'The provided HTML markup uses a value of unsupported type %s.' +
' This value must be coerced to a string before before using it here.',
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
export function checkFormFieldValueStringCoercion(value: mixed) {
if (__DEV__) {
if (willCoercionThrow(value)) {
console.error(
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not %s.' +
' This value must be coerced to a string before before using it here.',
typeName(value),
);
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}