Re-add the warning if PropType function is called manually (#8903)
* Revert "Revert "Warn if PropType function is called manually (#7132)"" This reverts commit be71f76ed8b7dbad25e3519455605252d3daf683. In other words, now we again have the warning if you attempt to call PropTypes manually. It was removed in #8066 but we shouldn't have done this since we still want to avoid people accidentally calling them in production (and even more so since now it would throw). Fixes #8080. * Pass secret in ReactControlledValuePropTypes * Record tests
Dan Abramov committed
Feb 8, 2017 at 16:29 UTC
1d90894f93d9564cdb6aec9ca3e3da7e1eb01b5a
6 files changed
+276
-8
scripts/fiber/tests-passing.txt
+10
@@ -313,9 +313,11 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
313
* should not warn for valid values
314
* should be implicitly optional and not warn without values
315
* should warn for missing required values
316
+* should warn if called manually in development
317
* should should accept any value
318
* should be implicitly optional and not warn without values
319
* should warn for missing required values
320
+* should warn if called manually in development
321
* should fail for invalid argument
322
* should support the arrayOf propTypes
323
* should support arrayOf with complex types
@@ -325,16 +327,19 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
327
* should not warn when passing an empty array
328
* should be implicitly optional and not warn without values
329
* should warn for missing required values
330
+* should warn if called manually in development
331
* should support components
332
* should not support multiple components or scalar values
333
* should be able to define a single child as label
334
* should warn when passing no label and isRequired is set
335
* should be implicitly optional and not warn without values
336
* should warn for missing required values
337
+* should warn if called manually in development
338
* should warn for invalid instances
339
* should not warn for valid values
340
* should be implicitly optional and not warn without values
341
* should warn for missing required values
342
+* should warn if called manually in development
343
* should warn for invalid values
344
* should not warn for valid values
345
* should not warn for iterables
@@ -342,6 +347,7 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
347
* should not warn for null/undefined if not required
348
* should warn for missing required values
349
* should accept empty array for required props
350
+* should warn if called manually in development
351
* should fail for invalid argument
352
* should support the objectOf propTypes
353
* should support objectOf with complex types
@@ -351,16 +357,19 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
357
* should not warn when passing an empty object
358
* should be implicitly optional and not warn without values
359
* should warn for missing required values
360
+* should warn if called manually in development
361
* should warn but not error for invalid argument
362
* should warn for invalid values
363
* should not warn for valid values
364
* should be implicitly optional and not warn without values
365
* should warn for missing required values
366
+* should warn if called manually in development
367
* should warn but not error for invalid argument
368
* should warn if none of the types are valid
369
* should not warn if one of the types are valid
370
* should be implicitly optional and not warn without values
371
* should warn for missing required values
372
+* should warn if called manually in development
373
* should warn for non objects
374
* should not warn for empty values
375
* should not warn for an empty object
@@ -371,6 +380,7 @@ src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
380
* should warn for invalid key types
381
* should be implicitly optional and not warn without values
382
* should warn for missing required values
383
+* should warn if called manually in development
384
* should warn for non-symbol
385
* should not warn for a polyfilled Symbol
386
* should have been called with the right params
src/isomorphic/classic/types/ReactPropTypes.js
+57
-7
@@ -13,6 +13,7 @@
13
14
var ReactElement = require('ReactElement');
15
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
16
+var ReactPropTypesSecret = require('ReactPropTypesSecret');
17
18
var emptyFunction = require('emptyFunction');
19
var getIteratorFn = require('getIteratorFn');
@@ -154,16 +155,42 @@ function PropTypeError(message) {
155
PropTypeError.prototype = Error.prototype;
156
157
function createChainableTypeChecker(validate) {
158
+ if (__DEV__) {
159
+ var manualPropTypeCallCache = {};
160
+ }
161
function checkType(
162
isRequired,
163
props,
164
propName,
165
componentName,
166
location,
163
- propFullName
167
+ propFullName,
168
+ secret
169
) {
170
componentName = componentName || ANONYMOUS;
171
propFullName = propFullName || propName;
172
+ if (__DEV__) {
173
+ if (
174
+ secret !== ReactPropTypesSecret &&
175
+ typeof console !== 'undefined'
176
+ ) {
177
+ var cacheKey = `${componentName}:${propName}`;
178
+ if (!manualPropTypeCallCache[cacheKey]) {
179
+ warning(
180
+ false,
181
+ 'You are manually calling a React.PropTypes validation ' +
182
+ 'function for the `%s` prop on `%s`. This is deprecated ' +
183
+ 'and will not work in production with the next major version. ' +
184
+ 'You may be seeing this warning due to a third-party PropTypes ' +
185
+ 'library. See https://fb.me/react-warning-dont-call-proptypes ' +
186
+ 'for details.',
187
+ propFullName,
188
+ componentName
189
+ );
190
+ manualPropTypeCallCache[cacheKey] = true;
191
+ }
192
+ }
193
+ }
194
if (props[propName] == null) {
195
var locationName = ReactPropTypeLocationNames[location];
196
if (isRequired) {
@@ -180,7 +207,13 @@ function createChainableTypeChecker(validate) {
207
}
208
return null;
209
} else {
183
- return validate(props, propName, componentName, location, propFullName);
210
+ return validate(
211
+ props,
212
+ propName,
213
+ componentName,
214
+ location,
215
+ propFullName,
216
+ );
217
}
218
}
219
@@ -191,7 +224,14 @@ function createChainableTypeChecker(validate) {
224
}
225
226
function createPrimitiveTypeChecker(expectedType) {
194
- function validate(props, propName, componentName, location, propFullName) {
227
+ function validate(
228
+ props,
229
+ propName,
230
+ componentName,
231
+ location,
232
+ propFullName,
233
+ secret
234
+ ) {
235
var propValue = props[propName];
236
var propType = getPropType(propValue);
237
if (propType !== expectedType) {
@@ -238,7 +278,8 @@ function createArrayOfTypeChecker(typeChecker) {
278
i,
279
componentName,
280
location,
241
- `${propFullName}[${i}]`
281
+ `${propFullName}[${i}]`,
282
+ ReactPropTypesSecret
283
);
284
if (error instanceof Error) {
285
return error;
@@ -329,7 +370,8 @@ function createObjectOfTypeChecker(typeChecker) {
370
key,
371
componentName,
372
location,
332
- `${propFullName}.${key}`
373
+ `${propFullName}.${key}`,
374
+ ReactPropTypesSecret
375
);
376
if (error instanceof Error) {
377
return error;
@@ -351,7 +393,14 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
393
for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
394
var checker = arrayOfTypeCheckers[i];
395
if (
354
- checker(props, propName, componentName, location, propFullName) == null
396
+ checker(
397
+ props,
398
+ propName,
399
+ componentName,
400
+ location,
401
+ propFullName,
402
+ ReactPropTypesSecret
403
+ ) == null
404
) {
405
return null;
406
}
@@ -401,7 +450,8 @@ function createShapeTypeChecker(shapeTypes) {
450
key,
451
componentName,
452
location,
404
- `${propFullName}.${key}`
453
+ `${propFullName}.${key}`,
454
+ ReactPropTypesSecret
455
);
456
if (error) {
457
return error;
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+187
@@ -15,6 +15,7 @@ var PropTypes;
15
var React;
16
var ReactFragment;
17
var ReactTestUtils;
18
+var ReactPropTypesSecret;
19
20
var Component;
21
var MyComponent;
@@ -26,6 +27,8 @@ function typeCheckFail(declaration, value, message) {
27
'testProp',
28
'testComponent',
29
'prop',
30
+ null,
31
+ ReactPropTypesSecret
32
);
33
expect(error instanceof Error).toBe(true);
34
expect(error.message).toBe(message);
@@ -42,6 +45,8 @@ function typeCheckFailRequiredValues(declaration) {
45
'testProp',
46
'testComponent',
47
'prop',
48
+ null,
49
+ ReactPropTypesSecret
50
);
51
expect(error1 instanceof Error).toBe(true);
52
expect(error1.message).toBe(specifiedButIsNullMsg);
@@ -51,6 +56,8 @@ function typeCheckFailRequiredValues(declaration) {
56
'testProp',
57
'testComponent',
58
'prop',
59
+ null,
60
+ ReactPropTypesSecret
61
);
62
expect(error2 instanceof Error).toBe(true);
63
expect(error2.message).toBe(unspecifiedMsg);
@@ -60,6 +67,8 @@ function typeCheckFailRequiredValues(declaration) {
67
'testProp',
68
'testComponent',
69
'prop',
70
+ null,
71
+ ReactPropTypesSecret
72
);
73
expect(error3 instanceof Error).toBe(true);
74
expect(error3.message).toBe(unspecifiedMsg);
@@ -72,16 +81,38 @@ function typeCheckPass(declaration, value) {
81
'testProp',
82
'testComponent',
83
'prop',
84
+ null,
85
+ ReactPropTypesSecret
86
);
87
expect(error).toBe(null);
88
}
89
90
+function expectWarningInDevelopment(declaration, value) {
91
+ var props = {testProp: value};
92
+ var propName = 'testProp' + Math.random().toString();
93
+ var componentName = 'testComponent' + Math.random().toString();
94
+ for (var i = 0; i < 3; i ++) {
95
+ declaration(
96
+ props,
97
+ propName,
98
+ componentName,
99
+ 'prop'
100
+ );
101
+ }
102
+ expect(console.error.calls.count()).toBe(1);
103
+ expect(console.error.calls.argsFor(0)[0]).toContain(
104
+ 'You are manually calling a React.PropTypes validation '
105
+ );
106
+ console.error.calls.reset();
107
+}
108
+
109
describe('ReactPropTypes', () => {
110
beforeEach(() => {
111
PropTypes = require('ReactPropTypes');
112
React = require('React');
113
ReactFragment = require('ReactFragment');
114
ReactTestUtils = require('ReactTestUtils');
115
+ ReactPropTypesSecret = require('ReactPropTypesSecret');
116
});
117
118
describe('Primitive Types', () => {
@@ -153,6 +184,52 @@ describe('ReactPropTypes', () => {
184
it('should warn for missing required values', () => {
185
typeCheckFailRequiredValues(PropTypes.string.isRequired);
186
});
187
+
188
+ it('should warn if called manually in development', () => {
189
+ spyOn(console, 'error');
190
+ expectWarningInDevelopment(PropTypes.array, /please/);
191
+ expectWarningInDevelopment(PropTypes.array, []);
192
+ expectWarningInDevelopment(PropTypes.array.isRequired, /please/);
193
+ expectWarningInDevelopment(PropTypes.array.isRequired, []);
194
+ expectWarningInDevelopment(PropTypes.array.isRequired, null);
195
+ expectWarningInDevelopment(PropTypes.array.isRequired, undefined);
196
+ expectWarningInDevelopment(PropTypes.bool, []);
197
+ expectWarningInDevelopment(PropTypes.bool, true);
198
+ expectWarningInDevelopment(PropTypes.bool.isRequired, []);
199
+ expectWarningInDevelopment(PropTypes.bool.isRequired, true);
200
+ expectWarningInDevelopment(PropTypes.bool.isRequired, null);
201
+ expectWarningInDevelopment(PropTypes.bool.isRequired, undefined);
202
+ expectWarningInDevelopment(PropTypes.func, false);
203
+ expectWarningInDevelopment(PropTypes.func, function() {});
204
+ expectWarningInDevelopment(PropTypes.func.isRequired, false);
205
+ expectWarningInDevelopment(PropTypes.func.isRequired, function() {});
206
+ expectWarningInDevelopment(PropTypes.func.isRequired, null);
207
+ expectWarningInDevelopment(PropTypes.func.isRequired, undefined);
208
+ expectWarningInDevelopment(PropTypes.number, function() {});
209
+ expectWarningInDevelopment(PropTypes.number, 42);
210
+ expectWarningInDevelopment(PropTypes.number.isRequired, function() {});
211
+ expectWarningInDevelopment(PropTypes.number.isRequired, 42);
212
+ expectWarningInDevelopment(PropTypes.number.isRequired, null);
213
+ expectWarningInDevelopment(PropTypes.number.isRequired, undefined);
214
+ expectWarningInDevelopment(PropTypes.string, 0);
215
+ expectWarningInDevelopment(PropTypes.string, 'foo');
216
+ expectWarningInDevelopment(PropTypes.string.isRequired, 0);
217
+ expectWarningInDevelopment(PropTypes.string.isRequired, 'foo');
218
+ expectWarningInDevelopment(PropTypes.string.isRequired, null);
219
+ expectWarningInDevelopment(PropTypes.string.isRequired, undefined);
220
+ expectWarningInDevelopment(PropTypes.symbol, 0);
221
+ expectWarningInDevelopment(PropTypes.symbol, Symbol('Foo'));
222
+ expectWarningInDevelopment(PropTypes.symbol.isRequired, 0);
223
+ expectWarningInDevelopment(PropTypes.symbol.isRequired, Symbol('Foo'));
224
+ expectWarningInDevelopment(PropTypes.symbol.isRequired, null);
225
+ expectWarningInDevelopment(PropTypes.symbol.isRequired, undefined);
226
+ expectWarningInDevelopment(PropTypes.object, '');
227
+ expectWarningInDevelopment(PropTypes.object, {foo: 'bar'});
228
+ expectWarningInDevelopment(PropTypes.object.isRequired, '');
229
+ expectWarningInDevelopment(PropTypes.object.isRequired, {foo: 'bar'});
230
+ expectWarningInDevelopment(PropTypes.object.isRequired, null);
231
+ expectWarningInDevelopment(PropTypes.object.isRequired, undefined);
232
+ });
233
});
234
235
describe('Any type', () => {
@@ -171,6 +248,13 @@ describe('ReactPropTypes', () => {
248
it('should warn for missing required values', () => {
249
typeCheckFailRequiredValues(PropTypes.any.isRequired);
250
});
251
+
252
+ it('should warn if called manually in development', () => {
253
+ spyOn(console, 'error');
254
+ expectWarningInDevelopment(PropTypes.any, null);
255
+ expectWarningInDevelopment(PropTypes.any.isRequired, null);
256
+ expectWarningInDevelopment(PropTypes.any.isRequired, undefined);
257
+ });
258
});
259
260
describe('ArrayOf Type', () => {
@@ -258,6 +342,24 @@ describe('ReactPropTypes', () => {
342
PropTypes.arrayOf(PropTypes.number).isRequired
343
);
344
});
345
+
346
+ it('should warn if called manually in development', () => {
347
+ spyOn(console, 'error');
348
+ expectWarningInDevelopment(
349
+ PropTypes.arrayOf({ foo: PropTypes.string }),
350
+ { foo: 'bar' }
351
+ );
352
+ expectWarningInDevelopment(
353
+ PropTypes.arrayOf(PropTypes.number),
354
+ [1, 2, 'b']
355
+ );
356
+ expectWarningInDevelopment(
357
+ PropTypes.arrayOf(PropTypes.number),
358
+ {'0': 'maybe-array', length: 1}
359
+ );
360
+ expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, null);
361
+ expectWarningInDevelopment(PropTypes.arrayOf(PropTypes.number).isRequired, undefined);
362
+ });
363
});
364
365
describe('Component Type', () => {
@@ -330,6 +432,18 @@ describe('ReactPropTypes', () => {
432
it('should warn for missing required values', () => {
433
typeCheckFailRequiredValues(PropTypes.element.isRequired);
434
});
435
+
436
+ it('should warn if called manually in development', () => {
437
+ spyOn(console, 'error');
438
+ expectWarningInDevelopment(PropTypes.element, [<div />, <div />]);
439
+ expectWarningInDevelopment(PropTypes.element, <div />);
440
+ expectWarningInDevelopment(PropTypes.element, 123);
441
+ expectWarningInDevelopment(PropTypes.element, 'foo');
442
+ expectWarningInDevelopment(PropTypes.element, false);
443
+ expectWarningInDevelopment(PropTypes.element.isRequired, null);
444
+ expectWarningInDevelopment(PropTypes.element.isRequired, undefined);
445
+ });
446
+
447
});
448
449
describe('Instance Types', () => {
@@ -404,6 +518,15 @@ describe('ReactPropTypes', () => {
518
it('should warn for missing required values', () => {
519
typeCheckFailRequiredValues(PropTypes.instanceOf(String).isRequired);
520
});
521
+
522
+ it('should warn if called manually in development', () => {
523
+ spyOn(console, 'error');
524
+ expectWarningInDevelopment(PropTypes.instanceOf(Date), {});
525
+ expectWarningInDevelopment(PropTypes.instanceOf(Date), new Date());
526
+ expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, {});
527
+ expectWarningInDevelopment(PropTypes.instanceOf(Date).isRequired, new Date());
528
+ });
529
+
530
});
531
532
describe('React Component Types', () => {
@@ -502,6 +625,16 @@ describe('ReactPropTypes', () => {
625
it('should accept empty array for required props', () => {
626
typeCheckPass(PropTypes.node.isRequired, []);
627
});
628
+
629
+ it('should warn if called manually in development', () => {
630
+ spyOn(console, 'error');
631
+ expectWarningInDevelopment(PropTypes.node, 'node');
632
+ expectWarningInDevelopment(PropTypes.node, {});
633
+ expectWarningInDevelopment(PropTypes.node.isRequired, 'node');
634
+ expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
635
+ expectWarningInDevelopment(PropTypes.node.isRequired, undefined);
636
+ });
637
+
638
});
639
640
describe('ObjectOf Type', () => {
@@ -604,6 +737,21 @@ describe('ReactPropTypes', () => {
737
PropTypes.objectOf(PropTypes.number).isRequired
738
);
739
});
740
+
741
+ it('should warn if called manually in development', () => {
742
+ spyOn(console, 'error');
743
+ expectWarningInDevelopment(
744
+ PropTypes.objectOf({ foo: PropTypes.string }),
745
+ { foo: 'bar' }
746
+ );
747
+ expectWarningInDevelopment(
748
+ PropTypes.objectOf(PropTypes.number),
749
+ {a: 1, b: 2, c: 'b'}
750
+ );
751
+ expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), [1, 2]);
752
+ expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), null);
753
+ expectWarningInDevelopment(PropTypes.objectOf(PropTypes.number), undefined);
754
+ });
755
});
756
757
describe('OneOf Types', () => {
@@ -660,6 +808,13 @@ describe('ReactPropTypes', () => {
808
it('should warn for missing required values', () => {
809
typeCheckFailRequiredValues(PropTypes.oneOf(['red', 'blue']).isRequired);
810
});
811
+
812
+ it('should warn if called manually in development', () => {
813
+ spyOn(console, 'error');
814
+ expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), true);
815
+ expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), null);
816
+ expectWarningInDevelopment(PropTypes.oneOf(['red', 'blue']), undefined);
817
+ });
818
});
819
820
describe('Union Types', () => {
@@ -724,6 +879,23 @@ describe('ReactPropTypes', () => {
879
PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
880
);
881
});
882
+
883
+ it('should warn if called manually in development', () => {
884
+ spyOn(console, 'error');
885
+ expectWarningInDevelopment(
886
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
887
+ []
888
+ );
889
+ expectWarningInDevelopment(
890
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
891
+ null
892
+ );
893
+ expectWarningInDevelopment(
894
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
895
+ undefined
896
+ );
897
+ });
898
+
899
});
900
901
describe('Shape Types', () => {
@@ -803,6 +975,21 @@ describe('ReactPropTypes', () => {
975
PropTypes.shape({key: PropTypes.number}).isRequired
976
);
977
});
978
+
979
+ it('should warn if called manually in development', () => {
980
+ spyOn(console, 'error');
981
+ expectWarningInDevelopment(PropTypes.shape({}), 'some string');
982
+ expectWarningInDevelopment(PropTypes.shape({ foo: PropTypes.number }), { foo: 42 });
983
+ expectWarningInDevelopment(
984
+ PropTypes.shape({key: PropTypes.number}).isRequired,
985
+ null
986
+ );
987
+ expectWarningInDevelopment(
988
+ PropTypes.shape({key: PropTypes.number}).isRequired,
989
+ undefined
990
+ );
991
+ expectWarningInDevelopment(PropTypes.element, <div />);
992
+ });
993
});
994
995
describe('Symbol Type', () => {
src/renderers/dom/shared/utils/ReactControlledValuePropTypes.js
+3
@@ -12,6 +12,7 @@
12
'use strict';
13
14
var React = require('React');
15
+var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
17
var warning = require('warning');
18
@@ -79,6 +80,8 @@ var ReactControlledValuePropTypes = {
80
propName,
81
tagName,
82
'prop',
83
+ null,
84
+ ReactPropTypesSecret,
85
);
86
}
87
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
src/shared/types/ReactPropTypesSecret.js
new
+17
@@ -0,0 +1,17 @@
1
+/**
2
+ * Copyright 2013-present, 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
+ * @flow
10
+ * @providesModule ReactPropTypesSecret
11
+ */
12
+
13
+'use strict';
14
+
15
+const ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
16
+
17
+module.exports = ReactPropTypesSecret;
src/shared/types/checkReactTypeSpec.js
+2
-1
@@ -12,6 +12,7 @@
12
'use strict';
13
14
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
15
+var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
17
var invariant = require('invariant');
18
var warning = require('warning');
@@ -74,7 +75,7 @@ function checkReactTypeSpec(
75
ReactPropTypeLocationNames[location],
76
typeSpecName
77
);
77
- error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location);
78
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
79
} catch (ex) {
80
error = ex;
81
}