Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81152 views
1
/**
2
* Copyright 2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule ReactElementValidator
10
*/
11
12
/**
13
* ReactElementValidator provides a wrapper around a element factory
14
* which validates the props passed to the element. This is intended to be
15
* used only in DEV and could be replaced by a static type checker for languages
16
* that support it.
17
*/
18
19
"use strict";
20
21
var ReactElement = require('ReactElement');
22
var ReactPropTypeLocations = require('ReactPropTypeLocations');
23
var ReactCurrentOwner = require('ReactCurrentOwner');
24
25
var monitorCodeUse = require('monitorCodeUse');
26
var warning = require('warning');
27
28
/**
29
* Warn if there's no key explicitly set on dynamic arrays of children or
30
* object keys are not valid. This allows us to keep track of children between
31
* updates.
32
*/
33
var ownerHasKeyUseWarning = {
34
'react_key_warning': {},
35
'react_numeric_key_warning': {}
36
};
37
var ownerHasMonitoredObjectMap = {};
38
39
var loggedTypeFailures = {};
40
41
var NUMERIC_PROPERTY_REGEX = /^\d+$/;
42
43
/**
44
* Gets the current owner's displayName for use in warnings.
45
*
46
* @internal
47
* @return {?string} Display name or undefined
48
*/
49
function getCurrentOwnerDisplayName() {
50
var current = ReactCurrentOwner.current;
51
return current && current.constructor.displayName || undefined;
52
}
53
54
/**
55
* Warn if the component doesn't have an explicit key assigned to it.
56
* This component is in an array. The array could grow and shrink or be
57
* reordered. All children that haven't already been validated are required to
58
* have a "key" property assigned to it.
59
*
60
* @internal
61
* @param {ReactComponent} component Component that requires a key.
62
* @param {*} parentType component's parent's type.
63
*/
64
function validateExplicitKey(component, parentType) {
65
if (component._store.validated || component.key != null) {
66
return;
67
}
68
component._store.validated = true;
69
70
warnAndMonitorForKeyUse(
71
'react_key_warning',
72
'Each child in an array should have a unique "key" prop.',
73
component,
74
parentType
75
);
76
}
77
78
/**
79
* Warn if the key is being defined as an object property but has an incorrect
80
* value.
81
*
82
* @internal
83
* @param {string} name Property name of the key.
84
* @param {ReactComponent} component Component that requires a key.
85
* @param {*} parentType component's parent's type.
86
*/
87
function validatePropertyKey(name, component, parentType) {
88
if (!NUMERIC_PROPERTY_REGEX.test(name)) {
89
return;
90
}
91
warnAndMonitorForKeyUse(
92
'react_numeric_key_warning',
93
'Child objects should have non-numeric keys so ordering is preserved.',
94
component,
95
parentType
96
);
97
}
98
99
/**
100
* Shared warning and monitoring code for the key warnings.
101
*
102
* @internal
103
* @param {string} warningID The id used when logging.
104
* @param {string} message The base warning that gets output.
105
* @param {ReactComponent} component Component that requires a key.
106
* @param {*} parentType component's parent's type.
107
*/
108
function warnAndMonitorForKeyUse(warningID, message, component, parentType) {
109
var ownerName = getCurrentOwnerDisplayName();
110
var parentName = parentType.displayName;
111
112
var useName = ownerName || parentName;
113
var memoizer = ownerHasKeyUseWarning[warningID];
114
if (memoizer.hasOwnProperty(useName)) {
115
return;
116
}
117
memoizer[useName] = true;
118
119
message += ownerName ?
120
` Check the render method of ${ownerName}.` :
121
` Check the renderComponent call using <${parentName}>.`;
122
123
// Usually the current owner is the offender, but if it accepts children as a
124
// property, it may be the creator of the child that's responsible for
125
// assigning it a key.
126
var childOwnerName = null;
127
if (component._owner && component._owner !== ReactCurrentOwner.current) {
128
// Name of the component that originally created this child.
129
childOwnerName = component._owner.constructor.displayName;
130
131
message += ` It was passed a child from ${childOwnerName}.`;
132
}
133
134
message += ' See http://fb.me/react-warning-keys for more information.';
135
monitorCodeUse(warningID, {
136
component: useName,
137
componentOwner: childOwnerName
138
});
139
console.warn(message);
140
}
141
142
/**
143
* Log that we're using an object map. We're considering deprecating this
144
* feature and replace it with proper Map and ImmutableMap data structures.
145
*
146
* @internal
147
*/
148
function monitorUseOfObjectMap() {
149
var currentName = getCurrentOwnerDisplayName() || '';
150
if (ownerHasMonitoredObjectMap.hasOwnProperty(currentName)) {
151
return;
152
}
153
ownerHasMonitoredObjectMap[currentName] = true;
154
monitorCodeUse('react_object_map_children');
155
}
156
157
/**
158
* Ensure that every component either is passed in a static location, in an
159
* array with an explicit keys property defined, or in an object literal
160
* with valid key property.
161
*
162
* @internal
163
* @param {*} component Statically passed child of any type.
164
* @param {*} parentType component's parent's type.
165
* @return {boolean}
166
*/
167
function validateChildKeys(component, parentType) {
168
if (Array.isArray(component)) {
169
for (var i = 0; i < component.length; i++) {
170
var child = component[i];
171
if (ReactElement.isValidElement(child)) {
172
validateExplicitKey(child, parentType);
173
}
174
}
175
} else if (ReactElement.isValidElement(component)) {
176
// This component was passed in a valid location.
177
component._store.validated = true;
178
} else if (component && typeof component === 'object') {
179
monitorUseOfObjectMap();
180
for (var name in component) {
181
validatePropertyKey(name, component[name], parentType);
182
}
183
}
184
}
185
186
/**
187
* Assert that the props are valid
188
*
189
* @param {string} componentName Name of the component for error messages.
190
* @param {object} propTypes Map of prop name to a ReactPropType
191
* @param {object} props
192
* @param {string} location e.g. "prop", "context", "child context"
193
* @private
194
*/
195
function checkPropTypes(componentName, propTypes, props, location) {
196
for (var propName in propTypes) {
197
if (propTypes.hasOwnProperty(propName)) {
198
var error;
199
// Prop type validation may throw. In case they do, we don't want to
200
// fail the render phase where it didn't fail before. So we log it.
201
// After these have been cleaned up, we'll let them throw.
202
try {
203
error = propTypes[propName](props, propName, componentName, location);
204
} catch (ex) {
205
error = ex;
206
}
207
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
208
// Only monitor this failure once because there tends to be a lot of the
209
// same error.
210
loggedTypeFailures[error.message] = true;
211
// This will soon use the warning module
212
monitorCodeUse(
213
'react_failed_descriptor_type_check',
214
{ message: error.message }
215
);
216
}
217
}
218
}
219
}
220
221
var ReactElementValidator = {
222
223
createElement: function(type, props, children) {
224
// We warn in this case but don't throw. We expect the element creation to
225
// succeed and there will likely be errors in render.
226
warning(
227
type != null,
228
'React.createElement: type should not be null or undefined. It should ' +
229
'be a string (for DOM elements) or a ReactClass (for composite ' +
230
'components).'
231
);
232
233
var element = ReactElement.createElement.apply(this, arguments);
234
235
// The result can be nullish if a mock or a custom function is used.
236
// TODO: Drop this when these are no longer allowed as the type argument.
237
if (element == null) {
238
return element;
239
}
240
241
for (var i = 2; i < arguments.length; i++) {
242
validateChildKeys(arguments[i], type);
243
}
244
245
if (type) {
246
var name = type.displayName;
247
if (type.propTypes) {
248
checkPropTypes(
249
name,
250
type.propTypes,
251
element.props,
252
ReactPropTypeLocations.prop
253
);
254
}
255
if (type.contextTypes) {
256
checkPropTypes(
257
name,
258
type.contextTypes,
259
element._context,
260
ReactPropTypeLocations.context
261
);
262
}
263
}
264
return element;
265
},
266
267
createFactory: function(type) {
268
var validatedFactory = ReactElementValidator.createElement.bind(
269
null,
270
type
271
);
272
validatedFactory.type = type;
273
return validatedFactory;
274
}
275
276
};
277
278
module.exports = ReactElementValidator;
279
280