Warn about setState inside getChildContext
Stack uses a module called ReactInvalidSetStateWarningHook, but all it does is warn about setState inside getChildContext. Doesn't seem worth keeping around, so I didn't use it for Fiber, but if I'm mistaken I'll change it.
Andrew Clark committed
Feb 7, 2017 at 14:19 UTC
75ee3c1424e0d44778512bf8fb1885eaa5fd1e9d
4 files changed
+46
-10
scripts/fiber/tests-passing-except-dev.txt
-1
@@ -106,7 +106,6 @@ src/renderers/__tests__/ReactComponentTreeHook-test.native.js
106
* does not report top-level wrapper as a root
107
108
src/renderers/__tests__/ReactCompositeComponent-test.js
109
-* should warn about `setState` in getChildContext
109
* should disallow nested render calls
110
111
src/renderers/__tests__/ReactHostOperationHistoryHook-test.js
scripts/fiber/tests-passing.txt
+1
@@ -578,6 +578,7 @@ src/renderers/__tests__/ReactCompositeComponent-test.js
578
* should warn about `setState` on unmounted components
579
* should silently allow `setState`, not call cb on unmounting components
580
* should warn about `setState` in render
581
+* should warn about `setState` in getChildContext
582
* should cleanup even if render() fatals
583
* should call componentWillUnmount before unmounting
584
* should warn when shouldComponentUpdate() returns undefined
src/renderers/shared/fiber/ReactFiberContext.js
+24
@@ -47,6 +47,21 @@ let didPerformWorkStackCursor : StackCursor<boolean> = createCursor(false);
47
// pushed the next context provider, and now need to merge their contexts.
48
let previousContext : Object = emptyObject;
49
50
+if (__DEV__) {
51
+ var _isProcessingChildContext = false;
52
+ var onBeginProcessingChildContext = function() {
53
+ _isProcessingChildContext = true;
54
+ };
55
+ exports.onBeginProcessingChildContext = onBeginProcessingChildContext;
56
+ var onEndProcessingChildContext = function() {
57
+ _isProcessingChildContext = false;
58
+ };
59
+ exports.onEndProcessingChildContext = onEndProcessingChildContext;
60
+ exports.isProcessingChildContext = function() : boolean {
61
+ return _isProcessingChildContext;
62
+ };
63
+}
64
+
65
function getUnmaskedContext(workInProgress : Fiber) : Object {
66
const hasOwnContext = isContextProvider(workInProgress);
67
if (hasOwnContext) {
@@ -144,6 +159,10 @@ exports.pushTopLevelContextObject = function(fiber : Fiber, context : Object, di
159
};
160
161
function processChildContext(fiber : Fiber, parentContext : Object, isReconciling : boolean): Object {
162
+ if (__DEV__) {
163
+ onBeginProcessingChildContext();
164
+ }
165
+
166
const instance = fiber.stateNode;
167
const childContextTypes = fiber.type.childContextTypes;
168
@@ -189,6 +208,11 @@ function processChildContext(fiber : Fiber, parentContext : Object, isReconcilin
208
checkReactTypeSpec(childContextTypes, childContext, 'child context', name);
209
ReactDebugCurrentFrame.current = null;
210
}
211
+
212
+ if (__DEV__) {
213
+ onEndProcessingChildContext();
214
+ }
215
+
216
return {...parentContext, ...childContext};
217
}
218
exports.processChildContext = processChildContext;
src/renderers/shared/fiber/ReactFiberScheduler.js
+21
-9
@@ -90,6 +90,10 @@ if (__DEV__) {
90
var warning = require('warning');
91
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
92
var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
93
+ var {
94
+ isProcessingChildContext,
95
+ onEndProcessingChildContext,
96
+ } = require('ReactFiberContext');
97
98
var warnAboutUpdateOnUnmounted = function(instance : ReactClass<any>) {
99
const ctor = instance.constructor;
@@ -103,14 +107,21 @@ if (__DEV__) {
107
);
108
};
109
106
- var warnAboutUpdateInRender = function(instance : ReactClass<any>) {
107
- warning(
108
- ReactCurrentOwner.current == null,
109
- 'Cannot update during an existing state transition (such as within ' +
110
- '`render` or another component\'s constructor). Render methods should ' +
111
- 'be a pure function of props and state; constructor side-effects are ' +
112
- 'an anti-pattern, but can be moved to `componentWillMount`.'
113
- );
110
+ var warnAboutInvalidUpdates = function(instance : ReactClass<any>) {
111
+ if (isProcessingChildContext()) {
112
+ warning(
113
+ false,
114
+ 'setState(...): Cannot call setState() inside getChildContext()',
115
+ );
116
+ } else if (ReactCurrentOwner.current != null) {
117
+ warning(
118
+ false,
119
+ 'Cannot update during an existing state transition (such as within ' +
120
+ '`render` or another component\'s constructor). Render methods should ' +
121
+ 'be a pure function of props and state; constructor side-effects are ' +
122
+ 'an anti-pattern, but can be moved to `componentWillMount`.'
123
+ );
124
+ }
125
};
126
}
127
@@ -869,6 +880,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
880
ReactCurrentOwner.current = null;
881
if (__DEV__) {
882
ReactDebugCurrentFiber.current = null;
883
+ onEndProcessingChildContext();
884
}
885
// It is no longer valid because this unit of work failed.
886
nextUnitOfWork = null;
@@ -1115,7 +1127,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
1127
if (__DEV__) {
1128
if (fiber.tag === ClassComponent) {
1129
const instance = fiber.stateNode;
1118
- warnAboutUpdateInRender(instance);
1130
+ warnAboutInvalidUpdates(instance);
1131
}
1132
}
1133