Validate that update callback is a function (#8639)
Dan Abramov committed
Dec 27, 2016 at 08:38 UTC
119294dcae46edd0d19ff7e7d6d25062ea68001a
3 files changed
+37
-3
scripts/fiber/tests-failing.txt
-3
@@ -86,9 +86,6 @@ src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
86
87
src/renderers/shared/shared/__tests__/ReactUpdates-test.js
88
* marks top-level updates
89
-* throws in setState if the update callback is not a function
90
-* throws in replaceState if the update callback is not a function
91
-* throws in forceUpdate if the update callback is not a function
89
90
src/renderers/shared/shared/__tests__/refs-test.js
91
* Should increase refs with an increase in divs
scripts/fiber/tests-passing.txt
+3
@@ -1564,6 +1564,9 @@ src/renderers/shared/shared/__tests__/ReactUpdates-test.js
1564
* should queue updates from during mount
1565
* calls componentWillReceiveProps setState callback properly
1566
* does not call render after a component as been deleted
1567
+* throws in setState if the update callback is not a function
1568
+* throws in replaceState if the update callback is not a function
1569
+* throws in forceUpdate if the update callback is not a function
1570
* does not update one component twice in a batch (#2410)
1571
* does not update one component twice in a batch (#6371)
1572
* unstable_batchedUpdates should return value from a callback
src/renderers/shared/fiber/ReactFiberClassComponent.js
+34
@@ -33,6 +33,31 @@ var invariant = require('invariant');
33
34
const isArray = Array.isArray;
35
36
+function formatUnexpectedArgument(arg) {
37
+ var type = typeof arg;
38
+ if (type !== 'object') {
39
+ return type;
40
+ }
41
+ var displayName = arg.constructor && arg.constructor.name || type;
42
+ var keys = Object.keys(arg);
43
+ if (keys.length > 0 && keys.length < 20) {
44
+ return `${displayName} (keys: ${keys.join(', ')})`;
45
+ }
46
+ return displayName;
47
+}
48
+
49
+function validateCallback(callback, callerName) {
50
+ if (typeof callback !== 'function') {
51
+ invariant(
52
+ false,
53
+ '%s(...): Expected the last optional `callback` argument to be a ' +
54
+ 'function. Instead received: %s.',
55
+ callerName,
56
+ formatUnexpectedArgument(callback)
57
+ );
58
+ }
59
+}
60
+
61
module.exports = function(
62
scheduleUpdate : (fiber : Fiber, priorityLevel : PriorityLevel) => void,
63
getPriorityContext : () => PriorityLevel,
@@ -42,18 +67,27 @@ module.exports = function(
67
const updater = {
68
isMounted,
69
enqueueSetState(instance, partialState, callback) {
70
+ if (callback) {
71
+ validateCallback(callback, 'setState');
72
+ }
73
const fiber = ReactInstanceMap.get(instance);
74
const priorityLevel = getPriorityContext();
75
addUpdate(fiber, partialState, callback || null, priorityLevel);
76
scheduleUpdate(fiber, priorityLevel);
77
},
78
enqueueReplaceState(instance, state, callback) {
79
+ if (callback) {
80
+ validateCallback(callback, 'replaceState');
81
+ }
82
const fiber = ReactInstanceMap.get(instance);
83
const priorityLevel = getPriorityContext();
84
addReplaceUpdate(fiber, state, callback || null, priorityLevel);
85
scheduleUpdate(fiber, priorityLevel);
86
},
87
enqueueForceUpdate(instance, callback) {
88
+ if (callback) {
89
+ validateCallback(callback, 'forceUpdate');
90
+ }
91
const fiber = ReactInstanceMap.get(instance);
92
const priorityLevel = getPriorityContext();
93
addForceUpdate(fiber, callback || null, priorityLevel);