[Hooks] Remove dispatch callbacks (#14037)
Removes the `enableDispatchCallback` feature flag and deletes the associated code. An earlier version of the Hooks proposal included this feature but we've since decided to remove it.
Andrew Clark committed
Oct 30, 2018 at 14:14 UTC
bf9fadfcf42847fa89f9a76c7d4dc2e2c17ee020
13 files changed
+33
-215
packages/react-dom/src/server/ReactPartialRendererHooks.js
+16
-20
@@ -12,22 +12,21 @@ import invariant from 'shared/invariant';
12
import warning from 'shared/warning';
13
14
type BasicStateAction<S> = (S => S) | S;
15
-type MaybeCallback<S> = void | null | (S => mixed);
16
-type Dispatch<S, A> = (A, MaybeCallback<S>) => void;
15
+type Dispatch<A> = A => void;
16
18
-type Update<S, A> = {
17
+type Update<A> = {
18
action: A,
20
- next: Update<S, A> | null,
19
+ next: Update<A> | null,
20
};
21
23
-type UpdateQueue<S, A> = {
24
- last: Update<S, A> | null,
22
+type UpdateQueue<A> = {
23
+ last: Update<A> | null,
24
dispatch: any,
25
};
26
27
type Hook = {
28
memoizedState: any,
30
- queue: UpdateQueue<any, any> | null,
29
+ queue: UpdateQueue<any> | null,
30
next: Hook | null,
31
};
32
@@ -39,10 +38,7 @@ let isReRender: boolean = false;
38
// Whether an update was scheduled during the currently executing render pass.
39
let didScheduleRenderPhaseUpdate: boolean = false;
40
// Lazily created map of render-phase updates
42
-let renderPhaseUpdates: Map<
43
- UpdateQueue<any, any>,
44
- Update<any, any>,
45
-> | null = null;
41
+let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
42
// Counter to prevent infinite loops.
43
let numberOfReRenders: number = 0;
44
const RE_RENDER_LIMIT = 25;
@@ -159,7 +155,7 @@ function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
155
156
export function useState<S>(
157
initialState: (() => S) | S,
162
-): [S, Dispatch<S, BasicStateAction<S>>] {
158
+): [S, Dispatch<BasicStateAction<S>>] {
159
return useReducer(
160
basicStateReducer,
161
// useReducer has a special case to support lazy useState initializers
@@ -171,14 +167,14 @@ export function useReducer<S, A>(
167
reducer: (S, A) => S,
168
initialState: S,
169
initialAction: A | void | null,
174
-): [S, Dispatch<S, A>] {
170
+): [S, Dispatch<A>] {
171
currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
172
workInProgressHook = createWorkInProgressHook();
173
if (isReRender) {
174
// This is a re-render. Apply the new render phase updates to the previous
175
// current hook.
180
- const queue: UpdateQueue<S, A> = (workInProgressHook.queue: any);
181
- const dispatch: Dispatch<S, A> = (queue.dispatch: any);
176
+ const queue: UpdateQueue<A> = (workInProgressHook.queue: any);
177
+ const dispatch: Dispatch<A> = (queue.dispatch: any);
178
if (renderPhaseUpdates !== null) {
179
// Render phase updates are stored in a map of queue -> linked list
180
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
@@ -211,11 +207,11 @@ export function useReducer<S, A>(
207
initialState = reducer(initialState, initialAction);
208
}
209
workInProgressHook.memoizedState = initialState;
214
- const queue: UpdateQueue<S, A> = (workInProgressHook.queue = {
210
+ const queue: UpdateQueue<A> = (workInProgressHook.queue = {
211
last: null,
212
dispatch: null,
213
});
218
- const dispatch: Dispatch<S, A> = (queue.dispatch = (dispatchAction.bind(
214
+ const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
215
null,
216
currentlyRenderingComponent,
217
queue,
@@ -294,9 +290,9 @@ export function useLayoutEffect(
290
);
291
}
292
297
-function dispatchAction<S, A>(
293
+function dispatchAction<A>(
294
componentIdentity: Object,
299
- queue: UpdateQueue<S, A>,
295
+ queue: UpdateQueue<A>,
296
action: A,
297
) {
298
invariant(
@@ -310,7 +306,7 @@ function dispatchAction<S, A>(
306
// queue -> linked list of updates. After this render pass, we'll restart
307
// and apply the stashed updates on top of the work-in-progress hook.
308
didScheduleRenderPhaseUpdate = true;
313
- const update: Update<S, A> = {
309
+ const update: Update<A> = {
310
action,
311
next: null,
312
};
packages/react-reconciler/src/ReactFiberCommitWork.js
-15
@@ -319,21 +319,6 @@ function commitLifeCycles(
319
case ForwardRef:
320
case SimpleMemoComponent: {
321
commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
322
- const newUpdateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
323
- if (newUpdateQueue !== null) {
324
- const callbackList = newUpdateQueue.callbackList;
325
- if (callbackList !== null) {
326
- newUpdateQueue.callbackList = null;
327
- for (let i = 0; i < callbackList.length; i++) {
328
- const update = callbackList[i];
329
- // Assume this is non-null, since otherwise it would not be part
330
- // of the callback list.
331
- const callback: () => mixed = (update.callback: any);
332
- update.callback = null;
333
- callback();
334
- }
335
- }
336
- }
322
break;
323
}
324
case ClassComponent: {
packages/react-reconciler/src/ReactFiberHooks.js
+17
-72
@@ -17,7 +17,6 @@ import {readContext} from './ReactFiberNewContext';
17
import {
18
Snapshot as SnapshotEffect,
19
Update as UpdateEffect,
20
- Callback as CallbackEffect,
20
Passive as PassiveEffect,
21
} from 'shared/ReactSideEffectTags';
22
import {
@@ -38,18 +37,15 @@ import {
37
38
import invariant from 'shared/invariant';
39
import warning from 'shared/warning';
41
-import warningWithoutStack from 'shared/warningWithoutStack';
42
-import {enableDispatchCallback_DEPRECATED} from 'shared/ReactFeatureFlags';
40
44
-type Update<S, A> = {
41
+type Update<A> = {
42
expirationTime: ExpirationTime,
43
action: A,
47
- callback: null | (S => mixed),
48
- next: Update<S, A> | null,
44
+ next: Update<A> | null,
45
};
46
51
-type UpdateQueue<S, A> = {
52
- last: Update<S, A> | null,
47
+type UpdateQueue<A> = {
48
+ last: Update<A> | null,
49
dispatch: any,
50
};
51
@@ -57,8 +53,8 @@ type Hook = {
53
memoizedState: any,
54
55
baseState: any,
60
- baseUpdate: Update<any, any> | null,
61
- queue: UpdateQueue<any, any> | null,
56
+ baseUpdate: Update<any> | null,
57
+ queue: UpdateQueue<any> | null,
58
59
next: Hook | null,
60
};
@@ -72,15 +68,12 @@ type Effect = {
68
};
69
70
export type FunctionComponentUpdateQueue = {
75
- callbackList: Array<Update<any, any>> | null,
71
lastEffect: Effect | null,
72
};
73
74
type BasicStateAction<S> = (S => S) | S;
75
81
-type MaybeCallback<S> = void | null | (S => mixed);
82
-
83
-type Dispatch<S, A> = (A, MaybeCallback<S>) => void;
76
+type Dispatch<A> = A => void;
77
78
// These are set right before calling the component.
79
let renderExpirationTime: ExpirationTime = NoWork;
@@ -113,10 +106,7 @@ let isReRender: boolean = false;
106
// Whether an update was scheduled during the currently executing render pass.
107
let didScheduleRenderPhaseUpdate: boolean = false;
108
// Lazily created map of render-phase updates
116
-let renderPhaseUpdates: Map<
117
- UpdateQueue<any, any>,
118
- Update<any, any>,
119
-> | null = null;
109
+let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
110
// Counter to prevent infinite loops.
111
let numberOfReRenders: number = 0;
112
const RE_RENDER_LIMIT = 25;
@@ -313,7 +303,6 @@ function createWorkInProgressHook(): Hook {
303
304
function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
305
return {
316
- callbackList: null,
306
lastEffect: null,
307
};
308
}
@@ -334,7 +323,7 @@ export function useContext<T>(
323
324
export function useState<S>(
325
initialState: (() => S) | S,
337
-): [S, Dispatch<S, BasicStateAction<S>>] {
326
+): [S, Dispatch<BasicStateAction<S>>] {
327
return useReducer(
328
basicStateReducer,
329
// useReducer has a special case to support lazy useState initializers
@@ -346,16 +335,16 @@ export function useReducer<S, A>(
335
reducer: (S, A) => S,
336
initialState: S,
337
initialAction: A | void | null,
349
-): [S, Dispatch<S, A>] {
338
+): [S, Dispatch<A>] {
339
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
340
workInProgressHook = createWorkInProgressHook();
352
- let queue: UpdateQueue<S, A> | null = (workInProgressHook.queue: any);
341
+ let queue: UpdateQueue<A> | null = (workInProgressHook.queue: any);
342
if (queue !== null) {
343
// Already have a queue, so this is an update.
344
if (isReRender) {
345
// This is a re-render. Apply the new render phase updates to the previous
346
// work-in-progress hook.
358
- const dispatch: Dispatch<S, A> = (queue.dispatch: any);
347
+ const dispatch: Dispatch<A> = (queue.dispatch: any);
348
if (renderPhaseUpdates !== null) {
349
// Render phase updates are stored in a map of queue -> linked list
350
const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
@@ -369,10 +358,6 @@ export function useReducer<S, A>(
358
// render's.
359
const action = update.action;
360
newState = reducer(newState, action);
372
- const callback = update.callback;
373
- if (callback !== null) {
374
- pushCallback(currentlyRenderingFiber, update);
375
- }
361
update = update.next;
362
} while (update !== null);
363
@@ -439,10 +424,6 @@ export function useReducer<S, A>(
424
// Process this update.
425
const action = update.action;
426
newState = reducer(newState, action);
442
- const callback = update.callback;
443
- if (callback !== null) {
444
- pushCallback(currentlyRenderingFiber, update);
445
- }
427
}
428
prevUpdate = update;
429
update = update.next;
@@ -458,7 +439,7 @@ export function useReducer<S, A>(
439
workInProgressHook.baseState = newBaseState;
440
}
441
461
- const dispatch: Dispatch<S, A> = (queue.dispatch: any);
442
+ const dispatch: Dispatch<A> = (queue.dispatch: any);
443
return [workInProgressHook.memoizedState, dispatch];
444
}
445
@@ -476,7 +457,7 @@ export function useReducer<S, A>(
457
last: null,
458
dispatch: null,
459
};
479
- const dispatch: Dispatch<S, A> = (queue.dispatch = (dispatchAction.bind(
460
+ const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
461
null,
462
currentlyRenderingFiber,
463
queue,
@@ -484,21 +465,6 @@ export function useReducer<S, A>(
465
return [workInProgressHook.memoizedState, dispatch];
466
}
467
487
-function pushCallback(workInProgress: Fiber, update: Update<any, any>): void {
488
- if (componentUpdateQueue === null) {
489
- componentUpdateQueue = createFunctionComponentUpdateQueue();
490
- componentUpdateQueue.callbackList = [update];
491
- } else {
492
- const callbackList = componentUpdateQueue.callbackList;
493
- if (callbackList === null) {
494
- componentUpdateQueue.callbackList = [update];
495
- } else {
496
- callbackList.push(update);
497
- }
498
- }
499
- workInProgress.effectTag |= CallbackEffect;
500
-}
501
-
468
function pushEffect(tag, create, destroy, inputs) {
469
const effect: Effect = {
470
tag,
@@ -677,26 +643,7 @@ export function useMemo<T>(
643
return nextValue;
644
}
645
680
-function dispatchAction<S, A>(
681
- fiber: Fiber,
682
- queue: UpdateQueue<S, A>,
683
- action: A,
684
- callback: void | null | (S => mixed),
685
-) {
686
- if (enableDispatchCallback_DEPRECATED) {
687
- if (__DEV__) {
688
- if (typeof callback === 'function') {
689
- warningWithoutStack(
690
- false,
691
- 'Update callbacks (the second argument to dispatch/setState) are ' +
692
- 'deprecated. Try useEffect instead.',
693
- );
694
- }
695
- }
696
- } else {
697
- callback = null;
698
- }
699
-
646
+function dispatchAction<A>(fiber: Fiber, queue: UpdateQueue<A>, action: A) {
647
invariant(
648
numberOfReRenders < RE_RENDER_LIMIT,
649
'Too many re-renders. React limits the number of renders to prevent ' +
@@ -712,10 +659,9 @@ function dispatchAction<S, A>(
659
// queue -> linked list of updates. After this render pass, we'll restart
660
// and apply the stashed updates on top of the work-in-progress hook.
661
didScheduleRenderPhaseUpdate = true;
715
- const update: Update<S, A> = {
662
+ const update: Update<A> = {
663
expirationTime: renderExpirationTime,
664
action,
718
- callback: callback !== undefined ? callback : null,
665
next: null,
666
};
667
if (renderPhaseUpdates === null) {
@@ -735,10 +681,9 @@ function dispatchAction<S, A>(
681
} else {
682
const currentTime = requestCurrentTime();
683
const expirationTime = computeExpirationForFiber(currentTime, fiber);
738
- const update: Update<S, A> = {
684
+ const update: Update<A> = {
685
expirationTime,
686
action,
741
- callback: callback !== undefined ? callback : null,
687
next: null,
688
};
689
flushPassiveEffects();
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
-99
@@ -61,7 +61,6 @@ describe('ReactHooksWithNoopRenderer', () => {
61
ReactFeatureFlags = require('shared/ReactFeatureFlags');
62
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
63
ReactFeatureFlags.enableHooks = true;
64
- ReactFeatureFlags.enableDispatchCallback_DEPRECATED = true;
64
ReactFeatureFlags.enableSchedulerTracing = true;
65
React = require('react');
66
ReactNoop = require('react-noop-renderer');
@@ -233,104 +232,6 @@ describe('ReactHooksWithNoopRenderer', () => {
232
expect(ReactNoop.flush()).toEqual(['Total: 7']);
233
});
234
236
- it('callbacks', () => {
237
- function Counter(props, ref) {
238
- const [count, updateCount] = useState(0);
239
- useImperativeMethods(ref, () => ({updateCount}));
240
- return <Text text={'Count: ' + count} />;
241
- }
242
- Counter = forwardRef(Counter);
243
- const counter = React.createRef(null);
244
- ReactNoop.render(<Counter ref={counter} />);
245
- ReactNoop.flush();
246
- expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
247
-
248
- expect(() => {
249
- counter.current.updateCount(7, count => {
250
- ReactNoop.yield(`Did update count`);
251
- });
252
- }).toWarnDev(
253
- 'Warning: Update callbacks (the second argument to ' +
254
- 'dispatch/setState) are deprecated. Try useEffect instead.',
255
- {withoutStack: true},
256
- );
257
-
258
- expect(ReactNoop.flush()).toEqual(['Count: 7', 'Did update count']);
259
-
260
- // Update twice in the same batch
261
- expect(() => {
262
- counter.current.updateCount(1, () => {
263
- ReactNoop.yield(`Did update count (first callback)`);
264
- });
265
- }).toWarnDev(
266
- 'Warning: Update callbacks (the second argument to ' +
267
- 'dispatch/setState) are deprecated. Try useEffect instead.',
268
- {withoutStack: true},
269
- );
270
-
271
- expect(() => {
272
- counter.current.updateCount(2, () => {
273
- ReactNoop.yield(`Did update count (second callback)`);
274
- });
275
- }).toWarnDev(
276
- 'Warning: Update callbacks (the second argument to ' +
277
- 'dispatch/setState) are deprecated. Try useEffect instead.',
278
- {withoutStack: true},
279
- );
280
-
281
- expect(ReactNoop.flush()).toEqual([
282
- // Component only renders once
283
- 'Count: 2',
284
- 'Did update count (first callback)',
285
- 'Did update count (second callback)',
286
- ]);
287
- });
288
-
289
- it('does not fire callbacks more than once when rebasing', () => {
290
- function Counter(props, ref) {
291
- const [count, updateCount] = useState(0);
292
- useImperativeMethods(ref, () => ({updateCount}));
293
- return <Text text={'Count: ' + count} />;
294
- }
295
- Counter = forwardRef(Counter);
296
- const counter = React.createRef(null);
297
- ReactNoop.render(<Counter ref={counter} />);
298
- ReactNoop.flush();
299
- expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
300
-
301
- expect(() => {
302
- counter.current.updateCount(1, count => {
303
- ReactNoop.yield(`Did update count (low pri)`);
304
- });
305
- }).toWarnDev(
306
- 'Warning: Update callbacks (the second argument to ' +
307
- 'dispatch/setState) are deprecated. Try useEffect instead.',
308
- {withoutStack: true},
309
- );
310
- ReactNoop.flushSync(() => {
311
- expect(() => {
312
- counter.current.updateCount(2, count => {
313
- ReactNoop.yield(`Did update count (high pri)`);
314
- });
315
- }).toWarnDev(
316
- 'Warning: Update callbacks (the second argument to ' +
317
- 'dispatch/setState) are deprecated. Try useEffect instead.',
318
- {withoutStack: true},
319
- );
320
- });
321
-
322
- expect(ReactNoop.clearYields()).toEqual([
323
- 'Count: 2',
324
- 'Did update count (high pri)',
325
- ]);
326
- // The high-pri update is processed again when we render at low priority,
327
- // but its callback should not fire again.
328
- expect(ReactNoop.flush()).toEqual([
329
- 'Count: 2',
330
- 'Did update count (low pri)',
331
- ]);
332
- });
333
-
235
it('returns the same updater function every time', () => {
236
let updaters = [];
237
function Counter() {
packages/shared/ReactFeatureFlags.js
-1
@@ -10,7 +10,6 @@
10
export const enableUserTimingAPI = __DEV__;
11
12
export const enableHooks = false;
13
-export const enableDispatchCallback_DEPRECATED = false;
13
// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
14
export const debugRenderPhaseSideEffects = false;
15
packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js
-1
@@ -16,7 +16,6 @@ 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;
19
export const warnAboutDeprecatedLifecycles = false;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
21
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js
-1
@@ -16,7 +16,6 @@ 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;
19
export const warnAboutDeprecatedLifecycles = false;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
21
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -15,7 +15,6 @@ 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,
18
debugRenderPhaseSideEffects,
19
debugRenderPhaseSideEffectsForStrictMode,
20
warnAboutDeprecatedLifecycles,
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -15,7 +15,6 @@ 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;
18
export const enableUserTimingAPI = __DEV__;
19
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
20
export const warnAboutDeprecatedLifecycles = false;
packages/shared/forks/ReactFeatureFlags.persistent.js
-1
@@ -16,7 +16,6 @@ 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;
19
export const warnAboutDeprecatedLifecycles = false;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
21
export const enableProfilerTimer = __PROFILE__;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -16,7 +16,6 @@ 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;
19
export const warnAboutDeprecatedLifecycles = false;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
21
export const enableProfilerTimer = false;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -16,7 +16,6 @@ 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;
19
export const warnAboutDeprecatedLifecycles = false;
20
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
21
export const enableProfilerTimer = false;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -18,7 +18,6 @@ export const {
18
replayFailedUnitOfWorkWithInvokeGuardedCallback,
19
warnAboutDeprecatedLifecycles,
20
disableInputAttributeSyncing,
21
- enableDispatchCallback_DEPRECATED,
21
} = require('ReactFeatureFlags');
22
23
// The rest of the flags are static for better dead code elimination.