@samitouri / QOS-React-2 / commits / f42431abe1

Revert "Remove renderPhaseUpdates Map (#17484)" (#17623)

This reverts commit 8a347ed024159a5307172ea633a4561160f4a6b9.

Dan Abramov committed Dec 16, 2019 at 19:03 UTC f42431abe1a31a3798989a6477422c492cf8dac1
4 files changed +133 -444
packages/react-reconciler/src/ReactFiberBeginWork.js
+2 -3
@@ -135,7 +135,7 @@ import {
135 calculateChangedBits,
136 scheduleWorkOnParentPath,
137 } from './ReactFiberNewContext';
138 -import {renderWithHooks, bailoutHooks} from './ReactFiberHooks';
138 +import {resetHooks, renderWithHooks, bailoutHooks} from './ReactFiberHooks';
139 import {stopProfilerTimerIfRunning} from './ReactProfilerTimer';
140 import {
141 getMaskedContext,
@@ -1318,8 +1318,7 @@ function mountIndeterminateComponent(
1318 workInProgress.tag = ClassComponent;
1319
1320 // Throw out any hooks that were used.
1321 - workInProgress.memoizedState = null;
1322 - workInProgress.updateQueue = null;
1321 + resetHooks();
1322
1323 // Push context providers early to prevent context stack mismatches.
1324 // During mounting we don't know the child context yet as the instance doesn't exist.
packages/react-reconciler/src/ReactFiberHooks.js
+128 -394
@@ -192,7 +192,13 @@ let workInProgressHook: Hook | null = null;
192
193 // Whether an update was scheduled during the currently executing render pass.
194 let didScheduleRenderPhaseUpdate: boolean = false;
195 -
195 +// Lazily created map of render-phase updates
196 +let renderPhaseUpdates: Map<
197 + UpdateQueue<any, any>,
198 + Update<any, any>,
199 +> | null = null;
200 +// Counter to prevent infinite loops.
201 +let numberOfReRenders: number = 0;
202 const RE_RENDER_LIMIT = 25;
203
204 // In DEV, this is the name of the currently executing primitive hook
@@ -386,6 +392,8 @@ export function renderWithHooks(
392 // workInProgressHook = null;
393
394 // didScheduleRenderPhaseUpdate = false;
395 + // renderPhaseUpdates = null;
396 + // numberOfReRenders = 0;
397
398 // TODO Warn if no hooks are used at all during mount, then some are used during update.
399 // Currently we will identify the update render as a mount because memoizedState === null.
@@ -417,17 +425,8 @@ export function renderWithHooks(
425 let children = Component(props, refOrContext);
426
427 if (didScheduleRenderPhaseUpdate) {
420 - // Counter to prevent infinite loops.
421 - let numberOfReRenders: number = 0;
428 do {
429 didScheduleRenderPhaseUpdate = false;
424 -
425 - invariant(
426 - numberOfReRenders < RE_RENDER_LIMIT,
427 - 'Too many re-renders. React limits the number of renders to prevent ' +
428 - 'an infinite loop.',
429 - );
430 -
430 numberOfReRenders += 1;
431 if (__DEV__) {
432 // Even when hot reloading, allow dependencies to stabilize
@@ -447,11 +446,14 @@ export function renderWithHooks(
446 }
447
448 ReactCurrentDispatcher.current = __DEV__
450 - ? HooksDispatcherOnRerenderInDEV
451 - : HooksDispatcherOnRerender;
449 + ? HooksDispatcherOnUpdateInDEV
450 + : HooksDispatcherOnUpdate;
451
452 children = Component(props, refOrContext);
453 } while (didScheduleRenderPhaseUpdate);
454 +
455 + renderPhaseUpdates = null;
456 + numberOfReRenders = 0;
457 }
458
459 // We can assume the previous dispatcher is always this one, since we set it
@@ -481,6 +483,8 @@ export function renderWithHooks(
483
484 // These were reset above
485 // didScheduleRenderPhaseUpdate = false;
486 + // renderPhaseUpdates = null;
487 + // numberOfReRenders = 0;
488
489 invariant(
490 !didRenderTooFewHooks,
@@ -503,31 +507,14 @@ export function bailoutHooks(
507 }
508 }
509
506 -export function resetHooksAfterThrow(): void {
510 +export function resetHooks(): void {
511 // We can assume the previous dispatcher is always this one, since we set it
512 // at the beginning of the render phase and there's no re-entrancy.
513 ReactCurrentDispatcher.current = ContextOnlyDispatcher;
514
511 - if (didScheduleRenderPhaseUpdate) {
512 - const current = (currentlyRenderingFiber: any).alternate;
513 - if (current !== null) {
514 - // There were render phase updates. These are only valid for this render
515 - // pass, which we are now aborting. Remove the updates from the queues so
516 - // they do not persist to the next render. We already did a single pass
517 - // through the whole list of hooks, so we know that any pending updates
518 - // must have been dispatched during the render phase. The ones that were
519 - // dispatched before we started rendering were already transferred to the
520 - // current hook's queue.
521 - let hook: Hook | null = current.memoizedState;
522 - while (hook !== null) {
523 - const queue = hook.queue;
524 - if (queue !== null) {
525 - queue.pending = null;
526 - }
527 - hook = hook.next;
528 - }
529 - }
530 - }
515 + // This is used to reset the state of this module when a component throws.
516 + // It's also called inside mountIndeterminateComponent if we determine the
517 + // component is a module-style component.
518
519 renderExpirationTime = NoWork;
520 currentlyRenderingFiber = (null: any);
@@ -543,6 +530,8 @@ export function resetHooksAfterThrow(): void {
530 }
531
532 didScheduleRenderPhaseUpdate = false;
533 + renderPhaseUpdates = null;
534 + numberOfReRenders = 0;
535 }
536
537 function mountWorkInProgressHook(): Hook {
@@ -678,6 +667,49 @@ function updateReducer<S, I, A>(
667
668 queue.lastRenderedReducer = reducer;
669
670 + if (numberOfReRenders > 0) {
671 + // This is a re-render. Apply the new render phase updates to the previous
672 + // work-in-progress hook.
673 + const dispatch: Dispatch<A> = (queue.dispatch: any);
674 + if (renderPhaseUpdates !== null) {
675 + // Render phase updates are stored in a map of queue -> linked list
676 + const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
677 + if (firstRenderPhaseUpdate !== undefined) {
678 + renderPhaseUpdates.delete(queue);
679 + let newState = hook.memoizedState;
680 + let update = firstRenderPhaseUpdate;
681 + do {
682 + // Process this render phase update. We don't have to check the
683 + // priority because it will always be the same as the current
684 + // render's.
685 + const action = update.action;
686 + newState = reducer(newState, action);
687 + update = update.next;
688 + } while (update !== null);
689 +
690 + // Mark that the fiber performed work, but only if the new state is
691 + // different from the current state.
692 + if (!is(newState, hook.memoizedState)) {
693 + markWorkInProgressReceivedUpdate();
694 + }
695 +
696 + hook.memoizedState = newState;
697 + // Don't persist the state accumulated from the render phase updates to
698 + // the base state unless the queue is empty.
699 + // TODO: Not sure if this is the desired semantics, but it's what we
700 + // do for gDSFP. I can't remember why.
701 + if (hook.baseQueue === null) {
702 + hook.baseState = newState;
703 + }
704 +
705 + queue.lastRenderedState = newState;
706 +
707 + return [newState, dispatch];
708 + }
709 + }
710 + return [hook.memoizedState, dispatch];
711 + }
712 +
713 const current: Hook = (currentHook: any);
714
715 // The last rebase update that is NOT part of the base state.
@@ -795,60 +827,6 @@ function updateReducer<S, I, A>(
827 return [hook.memoizedState, dispatch];
828 }
829
798 -function rerenderReducer<S, I, A>(
799 - reducer: (S, A) => S,
800 - initialArg: I,
801 - init?: I => S,
802 -): [S, Dispatch<A>] {
803 - const hook = updateWorkInProgressHook();
804 - const queue = hook.queue;
805 - invariant(
806 - queue !== null,
807 - 'Should have a queue. This is likely a bug in React. Please file an issue.',
808 - );
809 -
810 - queue.lastRenderedReducer = reducer;
811 -
812 - // This is a re-render. Apply the new render phase updates to the previous
813 - // work-in-progress hook.
814 - const dispatch: Dispatch<A> = (queue.dispatch: any);
815 - const lastRenderPhaseUpdate = queue.pending;
816 - let newState = hook.memoizedState;
817 - if (lastRenderPhaseUpdate !== null) {
818 - // The queue doesn't persist past this render pass.
819 - queue.pending = null;
820 -
821 - const firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
822 - let update = firstRenderPhaseUpdate;
823 - do {
824 - // Process this render phase update. We don't have to check the
825 - // priority because it will always be the same as the current
826 - // render's.
827 - const action = update.action;
828 - newState = reducer(newState, action);
829 - update = update.next;
830 - } while (update !== firstRenderPhaseUpdate);
831 -
832 - // Mark that the fiber performed work, but only if the new state is
833 - // different from the current state.
834 - if (!is(newState, hook.memoizedState)) {
835 - markWorkInProgressReceivedUpdate();
836 - }
837 -
838 - hook.memoizedState = newState;
839 - // Don't persist the state accumulated from the render phase updates to
840 - // the base state unless the queue is empty.
841 - // TODO: Not sure if this is the desired semantics, but it's what we
842 - // do for gDSFP. I can't remember why.
843 - if (hook.baseQueue === null) {
844 - hook.baseState = newState;
845 - }
846 -
847 - queue.lastRenderedState = newState;
848 - }
849 - return [newState, dispatch];
850 -}
851 -
830 function mountState<S>(
831 initialState: (() => S) | S,
832 ): [S, Dispatch<BasicStateAction<S>>] {
@@ -879,12 +857,6 @@ function updateState<S>(
857 return updateReducer(basicStateReducer, (initialState: any));
858 }
859
882 -function rerenderState<S>(
883 - initialState: (() => S) | S,
884 -): [S, Dispatch<BasicStateAction<S>>] {
885 - return rerenderReducer(basicStateReducer, (initialState: any));
886 -}
887 -
860 function pushEffect(tag, create, destroy, deps) {
861 const effect: Effect = {
862 tag,
@@ -1254,6 +1226,12 @@ function dispatchAction<S, A>(
1226 queue: UpdateQueue<S, A>,
1227 action: A,
1228 ) {
1229 + invariant(
1230 + numberOfReRenders < RE_RENDER_LIMIT,
1231 + 'Too many re-renders. React limits the number of renders to prevent ' +
1232 + 'an infinite loop.',
1233 + );
1234 +
1235 if (__DEV__) {
1236 if (typeof arguments[3] === 'function') {
1237 console.error(
@@ -1264,38 +1242,6 @@ function dispatchAction<S, A>(
1242 }
1243 }
1244
1267 - const currentTime = requestCurrentTimeForUpdate();
1268 - const suspenseConfig = requestCurrentSuspenseConfig();
1269 - const expirationTime = computeExpirationForFiber(
1270 - currentTime,
1271 - fiber,
1272 - suspenseConfig,
1273 - );
1274 -
1275 - const update: Update<S, A> = {
1276 - expirationTime,
1277 - suspenseConfig,
1278 - action,
1279 - eagerReducer: null,
1280 - eagerState: null,
1281 - next: (null: any),
1282 - };
1283 -
1284 - if (__DEV__) {
1285 - update.priority = getCurrentPriorityLevel();
1286 - }
1287 -
1288 - // Append the update to the end of the list.
1289 - const pending = queue.pending;
1290 - if (pending === null) {
1291 - // This is the first update. Create a circular list.
1292 - update.next = update;
1293 - } else {
1294 - update.next = pending.next;
1295 - pending.next = update;
1296 - }
1297 - queue.pending = update;
1298 -
1245 const alternate = fiber.alternate;
1246 if (
1247 fiber === currentlyRenderingFiber ||
@@ -1305,8 +1251,64 @@ function dispatchAction<S, A>(
1251 // queue -> linked list of updates. After this render pass, we'll restart
1252 // and apply the stashed updates on top of the work-in-progress hook.
1253 didScheduleRenderPhaseUpdate = true;
1308 - update.expirationTime = renderExpirationTime;
1254 + const update: Update<S, A> = {
1255 + expirationTime: renderExpirationTime,
1256 + suspenseConfig: null,
1257 + action,
1258 + eagerReducer: null,
1259 + eagerState: null,
1260 + next: (null: any),
1261 + };
1262 + if (__DEV__) {
1263 + update.priority = getCurrentPriorityLevel();
1264 + }
1265 + if (renderPhaseUpdates === null) {
1266 + renderPhaseUpdates = new Map();
1267 + }
1268 + const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
1269 + if (firstRenderPhaseUpdate === undefined) {
1270 + renderPhaseUpdates.set(queue, update);
1271 + } else {
1272 + // Append the update to the end of the list.
1273 + let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
1274 + while (lastRenderPhaseUpdate.next !== null) {
1275 + lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
1276 + }
1277 + lastRenderPhaseUpdate.next = update;
1278 + }
1279 } else {
1280 + const currentTime = requestCurrentTimeForUpdate();
1281 + const suspenseConfig = requestCurrentSuspenseConfig();
1282 + const expirationTime = computeExpirationForFiber(
1283 + currentTime,
1284 + fiber,
1285 + suspenseConfig,
1286 + );
1287 +
1288 + const update: Update<S, A> = {
1289 + expirationTime,
1290 + suspenseConfig,
1291 + action,
1292 + eagerReducer: null,
1293 + eagerState: null,
1294 + next: (null: any),
1295 + };
1296 +
1297 + if (__DEV__) {
1298 + update.priority = getCurrentPriorityLevel();
1299 + }
1300 +
1301 + // Append the update to the end of the list.
1302 + const pending = queue.pending;
1303 + if (pending === null) {
1304 + // This is the first update. Create a circular list.
1305 + update.next = update;
1306 + } else {
1307 + update.next = pending.next;
1308 + pending.next = update;
1309 + }
1310 + queue.pending = update;
1311 +
1312 if (
1313 fiber.expirationTime === NoWork &&
1314 (alternate === null || alternate.expirationTime === NoWork)
@@ -1411,31 +1413,11 @@ const HooksDispatcherOnUpdate: Dispatcher = {
1413 useTransition: updateTransition,
1414 };
1415
1414 -const HooksDispatcherOnRerender: Dispatcher = {
1415 - readContext,
1416 -
1417 - useCallback: updateCallback,
1418 - useContext: readContext,
1419 - useEffect: updateEffect,
1420 - useImperativeHandle: updateImperativeHandle,
1421 - useLayoutEffect: updateLayoutEffect,
1422 - useMemo: updateMemo,
1423 - useReducer: rerenderReducer,
1424 - useRef: updateRef,
1425 - useState: rerenderState,
1426 - useDebugValue: updateDebugValue,
1427 - useResponder: createResponderListener,
1428 - useDeferredValue: updateDeferredValue,
1429 - useTransition: updateTransition,
1430 -};
1431 -
1416 let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
1417 let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null;
1418 let HooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
1435 -let HooksDispatcherOnRerenderInDEV: Dispatcher | null = null;
1419 let InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher | null = null;
1420 let InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
1438 -let InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher | null = null;
1421
1422 if (__DEV__) {
1423 const warnInvalidContextAccess = () => {
@@ -1812,123 +1794,6 @@ if (__DEV__) {
1794 },
1795 };
1796
1815 - HooksDispatcherOnRerenderInDEV = {
1816 - readContext<T>(
1817 - context: ReactContext<T>,
1818 - observedBits: void | number | boolean,
1819 - ): T {
1820 - return readContext(context, observedBits);
1821 - },
1822 -
1823 - useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
1824 - currentHookNameInDev = 'useCallback';
1825 - updateHookTypesDev();
1826 - return updateCallback(callback, deps);
1827 - },
1828 - useContext<T>(
1829 - context: ReactContext<T>,
1830 - observedBits: void | number | boolean,
1831 - ): T {
1832 - currentHookNameInDev = 'useContext';
1833 - updateHookTypesDev();
1834 - return readContext(context, observedBits);
1835 - },
1836 - useEffect(
1837 - create: () => (() => void) | void,
1838 - deps: Array<mixed> | void | null,
1839 - ): void {
1840 - currentHookNameInDev = 'useEffect';
1841 - updateHookTypesDev();
1842 - return updateEffect(create, deps);
1843 - },
1844 - useImperativeHandle<T>(
1845 - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
1846 - create: () => T,
1847 - deps: Array<mixed> | void | null,
1848 - ): void {
1849 - currentHookNameInDev = 'useImperativeHandle';
1850 - updateHookTypesDev();
1851 - return updateImperativeHandle(ref, create, deps);
1852 - },
1853 - useLayoutEffect(
1854 - create: () => (() => void) | void,
1855 - deps: Array<mixed> | void | null,
1856 - ): void {
1857 - currentHookNameInDev = 'useLayoutEffect';
1858 - updateHookTypesDev();
1859 - return updateLayoutEffect(create, deps);
1860 - },
1861 - useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
1862 - currentHookNameInDev = 'useMemo';
1863 - updateHookTypesDev();
1864 - const prevDispatcher = ReactCurrentDispatcher.current;
1865 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
1866 - try {
1867 - return updateMemo(create, deps);
1868 - } finally {
1869 - ReactCurrentDispatcher.current = prevDispatcher;
1870 - }
1871 - },
1872 - useReducer<S, I, A>(
1873 - reducer: (S, A) => S,
1874 - initialArg: I,
1875 - init?: I => S,
1876 - ): [S, Dispatch<A>] {
1877 - currentHookNameInDev = 'useReducer';
1878 - updateHookTypesDev();
1879 - const prevDispatcher = ReactCurrentDispatcher.current;
1880 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
1881 - try {
1882 - return rerenderReducer(reducer, initialArg, init);
1883 - } finally {
1884 - ReactCurrentDispatcher.current = prevDispatcher;
1885 - }
1886 - },
1887 - useRef<T>(initialValue: T): {current: T} {
1888 - currentHookNameInDev = 'useRef';
1889 - updateHookTypesDev();
1890 - return updateRef(initialValue);
1891 - },
1892 - useState<S>(
1893 - initialState: (() => S) | S,
1894 - ): [S, Dispatch<BasicStateAction<S>>] {
1895 - currentHookNameInDev = 'useState';
1896 - updateHookTypesDev();
1897 - const prevDispatcher = ReactCurrentDispatcher.current;
1898 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
1899 - try {
1900 - return rerenderState(initialState);
1901 - } finally {
1902 - ReactCurrentDispatcher.current = prevDispatcher;
1903 - }
1904 - },
1905 - useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
1906 - currentHookNameInDev = 'useDebugValue';
1907 - updateHookTypesDev();
1908 - return updateDebugValue(value, formatterFn);
1909 - },
1910 - useResponder<E, C>(
1911 - responder: ReactEventResponder<E, C>,
1912 - props,
1913 - ): ReactEventResponderListener<E, C> {
1914 - currentHookNameInDev = 'useResponder';
1915 - updateHookTypesDev();
1916 - return createResponderListener(responder, props);
1917 - },
1918 - useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
1919 - currentHookNameInDev = 'useDeferredValue';
1920 - updateHookTypesDev();
1921 - return updateDeferredValue(value, config);
1922 - },
1923 - useTransition(
1924 - config: SuspenseConfig | void | null,
1925 - ): [(() => void) => void, boolean] {
1926 - currentHookNameInDev = 'useTransition';
1927 - updateHookTypesDev();
1928 - return updateTransition(config);
1929 - },
1930 - };
1931 -
1797 InvalidNestedHooksDispatcherOnMountInDEV = {
1798 readContext<T>(
1799 context: ReactContext<T>,
@@ -2190,135 +2055,4 @@ if (__DEV__) {
2055 return updateTransition(config);
2056 },
2057 };
2193 -
2194 - InvalidNestedHooksDispatcherOnRerenderInDEV = {
2195 - readContext<T>(
2196 - context: ReactContext<T>,
2197 - observedBits: void | number | boolean,
2198 - ): T {
2199 - warnInvalidContextAccess();
2200 - return readContext(context, observedBits);
2201 - },
2202 -
2203 - useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
2204 - currentHookNameInDev = 'useCallback';
2205 - warnInvalidHookAccess();
2206 - updateHookTypesDev();
2207 - return updateCallback(callback, deps);
2208 - },
2209 - useContext<T>(
2210 - context: ReactContext<T>,
2211 - observedBits: void | number | boolean,
2212 - ): T {
2213 - currentHookNameInDev = 'useContext';
2214 - warnInvalidHookAccess();
2215 - updateHookTypesDev();
2216 - return readContext(context, observedBits);
2217 - },
2218 - useEffect(
2219 - create: () => (() => void) | void,
2220 - deps: Array<mixed> | void | null,
2221 - ): void {
2222 - currentHookNameInDev = 'useEffect';
2223 - warnInvalidHookAccess();
2224 - updateHookTypesDev();
2225 - return updateEffect(create, deps);
2226 - },
2227 - useImperativeHandle<T>(
2228 - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
2229 - create: () => T,
2230 - deps: Array<mixed> | void | null,
2231 - ): void {
2232 - currentHookNameInDev = 'useImperativeHandle';
2233 - warnInvalidHookAccess();
2234 - updateHookTypesDev();
2235 - return updateImperativeHandle(ref, create, deps);
2236 - },
2237 - useLayoutEffect(
2238 - create: () => (() => void) | void,
2239 - deps: Array<mixed> | void | null,
2240 - ): void {
2241 - currentHookNameInDev = 'useLayoutEffect';
2242 - warnInvalidHookAccess();
2243 - updateHookTypesDev();
2244 - return updateLayoutEffect(create, deps);
2245 - },
2246 - useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
2247 - currentHookNameInDev = 'useMemo';
2248 - warnInvalidHookAccess();
2249 - updateHookTypesDev();
2250 - const prevDispatcher = ReactCurrentDispatcher.current;
2251 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
2252 - try {
2253 - return updateMemo(create, deps);
2254 - } finally {
2255 - ReactCurrentDispatcher.current = prevDispatcher;
2256 - }
2257 - },
2258 - useReducer<S, I, A>(
2259 - reducer: (S, A) => S,
2260 - initialArg: I,
2261 - init?: I => S,
2262 - ): [S, Dispatch<A>] {
2263 - currentHookNameInDev = 'useReducer';
2264 - warnInvalidHookAccess();
2265 - updateHookTypesDev();
2266 - const prevDispatcher = ReactCurrentDispatcher.current;
2267 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
2268 - try {
2269 - return rerenderReducer(reducer, initialArg, init);
2270 - } finally {
2271 - ReactCurrentDispatcher.current = prevDispatcher;
2272 - }
2273 - },
2274 - useRef<T>(initialValue: T): {current: T} {
2275 - currentHookNameInDev = 'useRef';
2276 - warnInvalidHookAccess();
2277 - updateHookTypesDev();
2278 - return updateRef(initialValue);
2279 - },
2280 - useState<S>(
2281 - initialState: (() => S) | S,
2282 - ): [S, Dispatch<BasicStateAction<S>>] {
2283 - currentHookNameInDev = 'useState';
2284 - warnInvalidHookAccess();
2285 - updateHookTypesDev();
2286 - const prevDispatcher = ReactCurrentDispatcher.current;
2287 - ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
2288 - try {
2289 - return rerenderState(initialState);
2290 - } finally {
2291 - ReactCurrentDispatcher.current = prevDispatcher;
2292 - }
2293 - },
2294 - useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
2295 - currentHookNameInDev = 'useDebugValue';
2296 - warnInvalidHookAccess();
2297 - updateHookTypesDev();
2298 - return updateDebugValue(value, formatterFn);
2299 - },
2300 - useResponder<E, C>(
2301 - responder: ReactEventResponder<E, C>,
2302 - props,
2303 - ): ReactEventResponderListener<E, C> {
2304 - currentHookNameInDev = 'useResponder';
2305 - warnInvalidHookAccess();
2306 - updateHookTypesDev();
2307 - return createResponderListener(responder, props);
2308 - },
2309 - useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
2310 - currentHookNameInDev = 'useDeferredValue';
2311 - warnInvalidHookAccess();
2312 - updateHookTypesDev();
2313 - return updateDeferredValue(value, config);
2314 - },
2315 - useTransition(
2316 - config: SuspenseConfig | void | null,
2317 - ): [(() => void) => void, boolean] {
2318 - currentHookNameInDev = 'useTransition';
2319 - warnInvalidHookAccess();
2320 - updateHookTypesDev();
2321 - return updateTransition(config);
2322 - },
2323 - };
2058 }
packages/react-reconciler/src/ReactFiberWorkLoop.js
+3 -3
@@ -140,7 +140,7 @@ import {
140 } from './ReactFiberCommitWork';
141 import {enqueueUpdate} from './ReactUpdateQueue';
142 import {resetContextDependencies} from './ReactFiberNewContext';
143 -import {resetHooksAfterThrow, ContextOnlyDispatcher} from './ReactFiberHooks';
143 +import {resetHooks, ContextOnlyDispatcher} from './ReactFiberHooks';
144 import {createCapturedValue} from './ReactCapturedValue';
145
146 import {
@@ -1280,7 +1280,7 @@ function handleError(root, thrownValue) {
1280 try {
1281 // Reset module-level state that was set during the render phase.
1282 resetContextDependencies();
1283 - resetHooksAfterThrow();
1283 + resetHooks();
1284 resetCurrentDebugFiberInDEV();
1285
1286 if (workInProgress === null || workInProgress.return === null) {
@@ -2634,7 +2634,7 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
2634 // Keep this code in sync with handleError; any changes here must have
2635 // corresponding changes there.
2636 resetContextDependencies();
2637 - resetHooksAfterThrow();
2637 + resetHooks();
2638 // Don't reset current debug fiber, since we're about to work on the
2639 // same fiber again.
2640
packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
-44
@@ -542,50 +542,6 @@ describe('ReactHooksWithNoopRenderer', () => {
542 ]);
543 expect(ReactNoop.getChildren()).toEqual([span(22)]);
544 });
545 -
546 - it('discards render phase updates if something suspends', () => {
547 - const thenable = {then() {}};
548 - function Foo({signal}) {
549 - return (
550 - <Suspense fallback="Loading...">
551 - <Bar signal={signal} />
552 - </Suspense>
553 - );
554 - }
555 -
556 - function Bar({signal: newSignal}) {
557 - let [counter, setCounter] = useState(0);
558 - let [signal, setSignal] = useState(true);
559 -
560 - // Increment a counter every time the signal changes
561 - if (signal !== newSignal) {
562 - setCounter(c => c + 1);
563 - setSignal(newSignal);
564 - if (counter === 0) {
565 - // We're suspending during a render that includes render phase
566 - // updates. Those updates should not persist to the next render.
567 - Scheduler.unstable_yieldValue('Suspend!');
568 - throw thenable;
569 - }
570 - }
571 -
572 - return <Text text={counter} />;
573 - }
574 -
575 - const root = ReactNoop.createRoot();
576 - root.render(<Foo signal={true} />);
577 -
578 - expect(Scheduler).toFlushAndYield([0]);
579 - expect(root).toMatchRenderedOutput(<span prop={0} />);
580 -
581 - root.render(<Foo signal={false} />);
582 - expect(Scheduler).toFlushAndYield(['Suspend!']);
583 - expect(root).toMatchRenderedOutput(<span prop={0} />);
584 -
585 - // Rendering again should suspend again.
586 - root.render(<Foo signal={false} />);
587 - expect(Scheduler).toFlushAndYield(['Suspend!']);
588 - });
545 });
546
547 describe('useReducer', () => {