@samitouri / QOS-React / commits / bf9fadfcf4

[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.