Disable hook update callback (2nd arg to setState/dispatch)
I put the feature behind a feature flag, along with a warning, so we can phase it out in www.
Andrew Clark committed
Oct 20, 2018 at 15:01 UTC
933b64710a48b2f86f71b1507901038025e79e3f
11 files changed
+75
-15
packages/react-reconciler/src/ReactFiberHooks.js
+16
@@ -37,6 +37,8 @@ import {
37
} from './ReactFiberScheduler';
38
39
import invariant from 'shared/invariant';
40
+import warningWithoutStack from 'shared/warningWithoutStack';
41
+import {enableDispatchCallback_DEPRECATED} from 'shared/ReactFeatureFlags';
42
43
type Update<S, A> = {
44
expirationTime: ExpirationTime,
@@ -680,6 +682,20 @@ function dispatchAction<S, A>(
682
action: A,
683
callback: void | null | (S => mixed),
684
) {
685
+ if (enableDispatchCallback_DEPRECATED) {
686
+ if (__DEV__) {
687
+ if (typeof callback === 'function') {
688
+ warningWithoutStack(
689
+ false,
690
+ 'Update callbacks (the second argument to dispatch/setState) are ' +
691
+ 'deprecated. Try useEffect instead.',
692
+ );
693
+ }
694
+ }
695
+ } else {
696
+ callback = null;
697
+ }
698
+
699
invariant(
700
numberOfReRenders < RE_RENDER_LIMIT,
701
'Too many re-renders. React limits the number of renders to prevent ' +
packages/react-reconciler/src/__tests__/ReactHooks-test.internal.js
+50
-15
@@ -58,6 +58,7 @@ describe('ReactHooks', () => {
58
ReactFeatureFlags = require('shared/ReactFeatureFlags');
59
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
60
ReactFeatureFlags.enableHooks = true;
61
+ ReactFeatureFlags.enableDispatchCallback_DEPRECATED = true;
62
ReactFeatureFlags.enableSchedulerTracing = true;
63
React = require('react');
64
ReactNoop = require('react-noop-renderer');
@@ -241,18 +242,39 @@ describe('ReactHooks', () => {
242
ReactNoop.flush();
243
expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
244
244
- counter.current.updateCount(7, count => {
245
- ReactNoop.yield(`Did update count`);
246
- });
245
+ expect(() => {
246
+ counter.current.updateCount(7, count => {
247
+ ReactNoop.yield(`Did update count`);
248
+ });
249
+ }).toWarnDev(
250
+ 'Warning: Update callbacks (the second argument to ' +
251
+ 'dispatch/setState) are deprecated. Try useEffect instead.',
252
+ {withoutStack: true},
253
+ );
254
+
255
expect(ReactNoop.flush()).toEqual(['Count: 7', 'Did update count']);
256
257
// Update twice in the same batch
250
- counter.current.updateCount(1, () => {
251
- ReactNoop.yield(`Did update count (first callback)`);
252
- });
253
- counter.current.updateCount(2, () => {
254
- ReactNoop.yield(`Did update count (second callback)`);
255
- });
258
+ expect(() => {
259
+ counter.current.updateCount(1, () => {
260
+ ReactNoop.yield(`Did update count (first callback)`);
261
+ });
262
+ }).toWarnDev(
263
+ 'Warning: Update callbacks (the second argument to ' +
264
+ 'dispatch/setState) are deprecated. Try useEffect instead.',
265
+ {withoutStack: true},
266
+ );
267
+
268
+ expect(() => {
269
+ counter.current.updateCount(2, () => {
270
+ ReactNoop.yield(`Did update count (second callback)`);
271
+ });
272
+ }).toWarnDev(
273
+ 'Warning: Update callbacks (the second argument to ' +
274
+ 'dispatch/setState) are deprecated. Try useEffect instead.',
275
+ {withoutStack: true},
276
+ );
277
+
278
expect(ReactNoop.flush()).toEqual([
279
// Component only renders once
280
'Count: 2',
@@ -273,14 +295,27 @@ describe('ReactHooks', () => {
295
ReactNoop.flush();
296
expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
297
276
- counter.current.updateCount(1, count => {
277
- ReactNoop.yield(`Did update count (low pri)`);
278
- });
279
- ReactNoop.flushSync(() => {
280
- counter.current.updateCount(2, count => {
281
- ReactNoop.yield(`Did update count (high pri)`);
298
+ expect(() => {
299
+ counter.current.updateCount(1, count => {
300
+ ReactNoop.yield(`Did update count (low pri)`);
301
});
302
+ }).toWarnDev(
303
+ 'Warning: Update callbacks (the second argument to ' +
304
+ 'dispatch/setState) are deprecated. Try useEffect instead.',
305
+ {withoutStack: true},
306
+ );
307
+ ReactNoop.flushSync(() => {
308
+ expect(() => {
309
+ counter.current.updateCount(2, count => {
310
+ ReactNoop.yield(`Did update count (high pri)`);
311
+ });
312
+ }).toWarnDev(
313
+ 'Warning: Update callbacks (the second argument to ' +
314
+ 'dispatch/setState) are deprecated. Try useEffect instead.',
315
+ {withoutStack: true},
316
+ );
317
});
318
+
319
expect(ReactNoop.clearYields()).toEqual([
320
'Count: 2',
321
'Did update count (high pri)',
packages/shared/ReactFeatureFlags.js
+1
@@ -10,6 +10,7 @@
10
export const enableUserTimingAPI = __DEV__;
11
12
export const enableHooks = false;
13
+export const enableDispatchCallback_DEPRECATED = false;
14
// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
15
export const debugRenderPhaseSideEffects = false;
16
packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js
+1
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableUserTimingAPI = __DEV__;
18
export const enableHooks = false;
19
+export const enableDispatchCallback_DEPRECATED = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
22
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js
+1
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableUserTimingAPI = __DEV__;
18
export const enableHooks = false;
19
+export const enableDispatchCallback_DEPRECATED = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
22
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -15,6 +15,7 @@ import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native-fb';
15
// Re-export dynamic flags from the fbsource version.
16
export const {
17
enableHooks,
18
+ enableDispatchCallback_DEPRECATED,
19
debugRenderPhaseSideEffects,
20
debugRenderPhaseSideEffectsForStrictMode,
21
warnAboutDeprecatedLifecycles,
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -15,6 +15,7 @@ import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native-oss';
15
export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableHooks = false;
18
+export const enableDispatchCallback_DEPRECATED = false;
19
export const enableUserTimingAPI = __DEV__;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
21
export const warnAboutDeprecatedLifecycles = false;
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableUserTimingAPI = __DEV__;
18
export const enableHooks = false;
19
+export const enableDispatchCallback_DEPRECATED = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
22
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableUserTimingAPI = __DEV__;
18
export const enableHooks = false;
19
+export const enableDispatchCallback_DEPRECATED = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
22
export const enableProfilerTimer = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
16
export const debugRenderPhaseSideEffectsForStrictMode = false;
17
export const enableUserTimingAPI = __DEV__;
18
export const enableHooks = false;
19
+export const enableDispatchCallback_DEPRECATED = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
22
export const enableProfilerTimer = false;
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -18,6 +18,7 @@ export const {
18
replayFailedUnitOfWorkWithInvokeGuardedCallback,
19
warnAboutDeprecatedLifecycles,
20
disableInputAttributeSyncing,
21
+ enableDispatchCallback_DEPRECATED,
22
} = require('ReactFeatureFlags');
23
24
// The rest of the flags are static for better dead code elimination.