diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 1fae90f07b1a..9f11fe6ecfda 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -7,9 +7,9 @@ * @flow */ -import type {Fiber} from './ReactInternalTypes'; -import type {Lanes} from './ReactFiberLane'; -import type {UpdateQueue} from './ReactFiberClassUpdateQueue'; +import type { Fiber } from './ReactInternalTypes'; +import type { Lanes } from './ReactFiberLane'; +import type { UpdateQueue } from './ReactFiberClassUpdateQueue'; import { LayoutStatic, @@ -22,15 +22,15 @@ import { enableSchedulingProfiler, } from 'shared/ReactFeatureFlags'; import ReactStrictModeWarnings from './ReactStrictModeWarnings'; -import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap'; +import { get as getInstance, set as setInstance } from 'shared/ReactInstanceMap'; import shallowEqual from 'shared/shallowEqual'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; import getComponentNameFromType from 'shared/getComponentNameFromType'; import assign from 'shared/assign'; import isArray from 'shared/isArray'; -import {REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE} from 'shared/ReactSymbols'; +import { REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE } from 'shared/ReactSymbols'; -import {NoMode, StrictLegacyMode, StrictEffectsMode} from './ReactTypeOfMode'; +import { NoMode, StrictLegacyMode, StrictEffectsMode } from './ReactTypeOfMode'; import { enqueueUpdate, @@ -45,7 +45,7 @@ import { cloneUpdateQueue, suspendIfUpdateReadFromEntangledAsyncAction, } from './ReactFiberClassUpdateQueue'; -import {NoLanes} from './ReactFiberLane'; +import { NoLanes } from './ReactFiberLane'; import { cacheContext, getMaskedContext, @@ -53,14 +53,18 @@ import { hasContextChanged, emptyContextObject, } from './ReactFiberLegacyContext'; -import {readContext, checkIfContextChanged} from './ReactFiberNewContext'; -import {requestUpdateLane, scheduleUpdateOnFiber} from './ReactFiberWorkLoop'; +import { readContext, checkIfContextChanged } from './ReactFiberNewContext'; +import { + requestUpdateLane, + scheduleUpdateOnFiber, + isAlreadyRendering, +} from './ReactFiberWorkLoop'; import { markForceUpdateScheduled, markStateUpdateScheduled, setIsStrictModeForDevtools, } from './ReactFiberDevToolsHook'; -import {startUpdateTimerByLane} from './ReactProfilerTimer'; +import { startUpdateTimerByLane } from './ReactProfilerTimer'; const fakeInternalInstance = {}; @@ -77,17 +81,17 @@ let didWarnAboutInvalidateContextType; let didWarnOnInvalidCallback; if (__DEV__) { - didWarnAboutStateAssignmentForComponent = new Set(); - didWarnAboutUninitializedState = new Set(); - didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set(); - didWarnAboutLegacyLifecyclesAndDerivedState = new Set(); - didWarnAboutDirectlyAssigningPropsToState = new Set(); - didWarnAboutUndefinedDerivedState = new Set(); - didWarnAboutContextTypeAndContextTypes = new Set(); - didWarnAboutContextTypes = new Set(); - didWarnAboutChildContextTypes = new Set(); - didWarnAboutInvalidateContextType = new Set(); - didWarnOnInvalidCallback = new Set(); + didWarnAboutStateAssignmentForComponent = new Set < string > (); + didWarnAboutUninitializedState = new Set < string > (); + didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set < string > (); + didWarnAboutLegacyLifecyclesAndDerivedState = new Set < string > (); + didWarnAboutDirectlyAssigningPropsToState = new Set < string > (); + didWarnAboutUndefinedDerivedState = new Set < string > (); + didWarnAboutContextTypeAndContextTypes = new Set < string > (); + didWarnAboutContextTypes = new Set < mixed > (); + didWarnAboutChildContextTypes = new Set < mixed > (); + didWarnAboutInvalidateContextType = new Set < string > (); + didWarnOnInvalidCallback = new Set < string > (); Object.freeze(fakeInternalInstance); } @@ -103,7 +107,7 @@ function warnOnInvalidCallback(callback: mixed) { didWarnOnInvalidCallback.add(key); console.error( 'Expected the last optional `callback` argument to be a ' + - 'function. Instead received: %s.', + 'function. Instead received: %s.', callback, ); } @@ -118,7 +122,7 @@ function warnOnUndefinedDerivedState(type: any, partialState: any) { didWarnAboutUndefinedDerivedState.add(componentName); console.error( '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + - 'You have returned undefined.', + 'You have returned undefined.', componentName, ); } @@ -179,7 +183,12 @@ const classComponentUpdater = { const root = enqueueUpdate(fiber, update, lane); if (root !== null) { - startUpdateTimerByLane(lane, 'this.setState()', fiber); + startUpdateTimerByLane( + lane, + 'this.setState()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, fiber, lane); entangleTransitions(root, fiber, lane); } @@ -205,7 +214,12 @@ const classComponentUpdater = { const root = enqueueUpdate(fiber, update, lane); if (root !== null) { - startUpdateTimerByLane(lane, 'this.replaceState()', fiber); + startUpdateTimerByLane( + lane, + 'this.replaceState()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, fiber, lane); entangleTransitions(root, fiber, lane); } @@ -231,7 +245,12 @@ const classComponentUpdater = { const root = enqueueUpdate(fiber, update, lane); if (root !== null) { - startUpdateTimerByLane(lane, 'this.forceUpdate()', fiber); + startUpdateTimerByLane( + lane, + 'this.forceUpdate()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, fiber, lane); entangleTransitions(root, fiber, lane); } @@ -275,7 +294,7 @@ function checkShouldComponentUpdate( if (shouldUpdate === undefined) { console.error( '%s.shouldComponentUpdate(): Returned undefined instead of a ' + - 'boolean value. Make sure to return true or false.', + 'boolean value. Make sure to return true or false.', getComponentNameFromType(ctor) || 'Component', ); } @@ -303,13 +322,13 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { if (ctor.prototype && typeof ctor.prototype.render === 'function') { console.error( 'No `render` method found on the %s ' + - 'instance: did you accidentally return an object from the constructor?', + 'instance: did you accidentally return an object from the constructor?', name, ); } else { console.error( 'No `render` method found on the %s ' + - 'instance: you may have forgotten to define `render`.', + 'instance: you may have forgotten to define `render`.', name, ); } @@ -322,8 +341,8 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { ) { console.error( 'getInitialState was defined on %s, a plain JavaScript class. ' + - 'This is only supported for classes created using React.createClass. ' + - 'Did you mean to define a state property instead?', + 'This is only supported for classes created using React.createClass. ' + + 'Did you mean to define a state property instead?', name, ); } @@ -333,15 +352,15 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { ) { console.error( 'getDefaultProps was defined on %s, a plain JavaScript class. ' + - 'This is only supported for classes created using React.createClass. ' + - 'Use a static property to define defaultProps instead.', + 'This is only supported for classes created using React.createClass. ' + + 'Use a static property to define defaultProps instead.', name, ); } if (instance.contextType) { console.error( 'contextType was defined as an instance property on %s. Use a static ' + - 'property to define contextType instead.', + 'property to define contextType instead.', name, ); } @@ -351,7 +370,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutChildContextTypes.add(ctor); console.error( '%s uses the legacy childContextTypes API which was removed in React 19. ' + - 'Use React.createContext() instead. (https://react.dev/link/legacy-context)', + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)', name, ); } @@ -359,8 +378,8 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutContextTypes.add(ctor); console.error( '%s uses the legacy contextTypes API which was removed in React 19. ' + - 'Use React.createContext() with static contextType instead. ' + - '(https://react.dev/link/legacy-context)', + 'Use React.createContext() with static contextType instead. ' + + '(https://react.dev/link/legacy-context)', name, ); } @@ -368,7 +387,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { if (instance.contextTypes) { console.error( 'contextTypes was defined as an instance property on %s. Use a static ' + - 'property to define contextTypes instead.', + 'property to define contextTypes instead.', name, ); } @@ -381,7 +400,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutContextTypeAndContextTypes.add(ctor); console.error( '%s declares both contextTypes and contextType static properties. ' + - 'The legacy contextTypes property will be ignored.', + 'The legacy contextTypes property will be ignored.', name, ); } @@ -389,7 +408,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutChildContextTypes.add(ctor); console.error( '%s uses the legacy childContextTypes API which will soon be removed. ' + - 'Use React.createContext() instead. (https://react.dev/link/legacy-context)', + 'Use React.createContext() instead. (https://react.dev/link/legacy-context)', name, ); } @@ -397,8 +416,8 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutContextTypes.add(ctor); console.error( '%s uses the legacy contextTypes API which will soon be removed. ' + - 'Use React.createContext() with static contextType instead. ' + - '(https://react.dev/link/legacy-context)', + 'Use React.createContext() with static contextType instead. ' + + '(https://react.dev/link/legacy-context)', name, ); } @@ -407,9 +426,9 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { if (typeof instance.componentShouldUpdate === 'function') { console.error( '%s has a method called ' + - 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - 'The name is phrased as a question because the function is ' + - 'expected to return a value.', + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + + 'The name is phrased as a question because the function is ' + + 'expected to return a value.', name, ); } @@ -420,40 +439,40 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { ) { console.error( '%s has a method called shouldComponentUpdate(). ' + - 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + - 'Please extend React.Component if shouldComponentUpdate is used.', + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentNameFromType(ctor) || 'A pure component', ); } if (typeof instance.componentDidUnmount === 'function') { console.error( '%s has a method called ' + - 'componentDidUnmount(). But there is no such lifecycle method. ' + - 'Did you mean componentWillUnmount()?', + 'componentDidUnmount(). But there is no such lifecycle method. ' + + 'Did you mean componentWillUnmount()?', name, ); } if (typeof instance.componentDidReceiveProps === 'function') { console.error( '%s has a method called ' + - 'componentDidReceiveProps(). But there is no such lifecycle method. ' + - 'If you meant to update the state in response to changing props, ' + - 'use componentWillReceiveProps(). If you meant to fetch data or ' + - 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + + 'If you meant to update the state in response to changing props, ' + + 'use componentWillReceiveProps(). If you meant to fetch data or ' + + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name, ); } if (typeof instance.componentWillRecieveProps === 'function') { console.error( '%s has a method called ' + - 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name, ); } if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') { console.error( '%s has a method called ' + - 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', + 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', name, ); } @@ -461,14 +480,14 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { if (instance.props !== undefined && hasMutatedProps) { console.error( 'When calling super() in `%s`, make sure to pass ' + - "up the same props that your component's constructor was passed.", + "up the same props that your component's constructor was passed.", name, ); } if (instance.defaultProps) { console.error( 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' + - ' Instead, define defaultProps as a static property on %s.', + ' Instead, define defaultProps as a static property on %s.', name, name, ); @@ -482,7 +501,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor); console.error( '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' + - 'This component defines getSnapshotBeforeUpdate() only.', + 'This component defines getSnapshotBeforeUpdate() only.', getComponentNameFromType(ctor), ); } @@ -490,21 +509,21 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { if (typeof instance.getDerivedStateFromProps === 'function') { console.error( '%s: getDerivedStateFromProps() is defined as an instance method ' + - 'and will be ignored. Instead, declare it as a static method.', + 'and will be ignored. Instead, declare it as a static method.', name, ); } if (typeof instance.getDerivedStateFromError === 'function') { console.error( '%s: getDerivedStateFromError() is defined as an instance method ' + - 'and will be ignored. Instead, declare it as a static method.', + 'and will be ignored. Instead, declare it as a static method.', name, ); } if (typeof ctor.getSnapshotBeforeUpdate === 'function') { console.error( '%s: getSnapshotBeforeUpdate() is defined as a static method ' + - 'and will be ignored. Instead, declare it as an instance method.', + 'and will be ignored. Instead, declare it as an instance method.', name, ); } @@ -518,7 +537,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) { ) { console.error( '%s.getChildContext(): childContextTypes must be defined in order to ' + - 'use getChildContext().', + 'use getChildContext().', name, ); } @@ -565,7 +584,7 @@ function constructClassInstance( } console.error( '%s defines an invalid contextType. ' + - 'contextType should point to the Context object returned by React.createContext().%s', + 'contextType should point to the Context object returned by React.createContext().%s', getComponentNameFromType(ctor) || 'Component', addendum, ); @@ -617,9 +636,9 @@ function constructClassInstance( didWarnAboutUninitializedState.add(componentName); console.error( '`%s` uses `getDerivedStateFromProps` but its initial state is ' + - '%s. This is not recommended. Instead, define the initial state by ' + - 'assigning an object to `this.state` in the constructor of `%s`. ' + - 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', + '%s. This is not recommended. Instead, define the initial state by ' + + 'assigning an object to `this.state` in the constructor of `%s`. ' + + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', componentName, instance.state === null ? 'null' : 'undefined', componentName, @@ -677,9 +696,9 @@ function constructClassInstance( didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName); console.error( 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + - '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' + - 'The above lifecycles should be removed. Learn more about this warning here:\n' + - 'https://react.dev/link/unsafe-component-lifecycles', + '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' + + 'The above lifecycles should be removed. Learn more about this warning here:\n' + + 'https://react.dev/link/unsafe-component-lifecycles', componentName, newApiName, foundWillMountName !== null ? `\n ${foundWillMountName}` : '', @@ -716,8 +735,8 @@ function callComponentWillMount(workInProgress: Fiber, instance: any) { if (__DEV__) { console.error( '%s.componentWillMount(): Assigning directly to this.state is ' + - "deprecated (except inside a component's " + - 'constructor). Use setState instead.', + "deprecated (except inside a component's " + + 'constructor). Use setState instead.', getComponentNameFromFiber(workInProgress) || 'Component', ); } @@ -747,8 +766,8 @@ function callComponentWillReceiveProps( didWarnAboutStateAssignmentForComponent.add(componentName); console.error( '%s.componentWillReceiveProps(): Assigning directly to ' + - "this.state is deprecated (except inside a component's " + - 'constructor). Use setState instead.', + "this.state is deprecated (except inside a component's " + + 'constructor). Use setState instead.', componentName, ); } @@ -792,8 +811,8 @@ function mountClassInstance( didWarnAboutDirectlyAssigningPropsToState.add(componentName); console.error( '%s: It is not recommended to assign props directly to state ' + - "because updates to props won't be reflected in state. " + - 'In most cases, it is better to use props directly.', + "because updates to props won't be reflected in state. " + + 'In most cases, it is better to use props directly.', componentName, ); } diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 0450495ff0d4..5b0aaa27b956 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -22,11 +22,11 @@ import type { HookType, MemoCache, } from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; -import type {HookFlags} from './ReactHookEffectTags'; -import type {Flags} from './ReactFiberFlags'; -import type {TransitionStatus} from './ReactFiberConfig'; -import type {ScheduledGesture} from './ReactFiberGestureScheduler'; +import type { Lanes, Lane } from './ReactFiberLane'; +import type { HookFlags } from './ReactHookEffectTags'; +import type { Flags } from './ReactFiberFlags'; +import type { TransitionStatus } from './ReactFiberConfig'; +import type { ScheduledGesture } from './ReactFiberGestureScheduler'; import { HostTransitionContext, @@ -78,8 +78,8 @@ import { ContinuousEventPriority, higherEventPriority, } from './ReactEventPriorities'; -import {readContext, checkIfContextChanged} from './ReactFiberNewContext'; -import {HostRoot, CacheComponent, HostComponent} from './ReactWorkTags'; +import { readContext, checkIfContextChanged } from './ReactFiberNewContext'; +import { HostRoot, CacheComponent, HostComponent } from './ReactWorkTags'; import { LayoutStatic as LayoutStaticEffect, Passive as PassiveEffect, @@ -105,6 +105,7 @@ import { requestDeferredLane, markSkippedUpdateLanes, isInvalidExecutionContextForEventFunction, + isAlreadyRendering, } from './ReactFiberWorkLoop'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; @@ -126,7 +127,7 @@ import { startUpdateTimerByLane, startHostActionTimer, } from './ReactProfilerTimer'; -import {createCache} from './ReactFiberCacheComponent'; +import { createCache } from './ReactFiberCacheComponent'; import { createUpdate as createLegacyQueueUpdate, enqueueUpdate as enqueueLegacyQueueUpdate, @@ -137,8 +138,8 @@ import { enqueueConcurrentHookUpdateAndEagerlyBailout, enqueueConcurrentRenderForLane, } from './ReactFiberConcurrentUpdates'; -import {getTreeId} from './ReactFiberTreeContext'; -import {now} from './Scheduler'; +import { getTreeId } from './ReactFiberTreeContext'; +import { now } from './Scheduler'; import { trackUsedThenable, checkIfUseWrappedInTryCatch, @@ -146,20 +147,20 @@ import { SuspenseException, SuspenseActionException, } from './ReactFiberThenable'; -import type {ThenableState} from './ReactFiberThenable'; -import type {Transition} from 'react/src/ReactStartTransition'; +import type { ThenableState } from './ReactFiberThenable'; +import type { Transition } from 'react/src/ReactStartTransition'; import { peekEntangledActionLane, peekEntangledActionThenable, chainThenableValue, } from './ReactFiberAsyncAction'; -import {requestTransitionLane} from './ReactFiberRootScheduler'; -import {isCurrentTreeHidden} from './ReactFiberHiddenContext'; -import {requestCurrentTransition} from './ReactFiberTransition'; +import { requestTransitionLane } from './ReactFiberRootScheduler'; +import { isCurrentTreeHidden } from './ReactFiberHiddenContext'; +import { requestCurrentTransition } from './ReactFiberTransition'; -import {callComponentInDEV} from './ReactFiberCallUserSpace'; +import { callComponentInDEV } from './ReactFiberCallUserSpace'; -import {scheduleGesture} from './ReactFiberGestureScheduler'; +import { scheduleGesture } from './ReactFiberGestureScheduler'; export type Update = { lane: Lane, @@ -175,8 +176,8 @@ export type UpdateQueue = { pending: Update | null, lanes: Lanes, dispatch: (A => mixed) | null, - lastRenderedReducer: ((S, A) => S) | null, - lastRenderedState: S | null, + lastRenderedReducer: ((S, A) => S) | null, + lastRenderedState: S | null, }; let didWarnAboutMismatchedHooksForComponent; @@ -185,10 +186,10 @@ let didWarnAboutUseWrappedInTryCatch; let didWarnAboutAsyncClientComponent; let didWarnAboutUseFormState; if (__DEV__) { - didWarnAboutMismatchedHooksForComponent = new Set(); - didWarnAboutUseWrappedInTryCatch = new Set(); - didWarnAboutAsyncClientComponent = new Set(); - didWarnAboutUseFormState = new Set(); + didWarnAboutMismatchedHooksForComponent = new Set < string | null > (); + didWarnAboutUseWrappedInTryCatch = new Set < string | null > (); + didWarnAboutAsyncClientComponent = new Set < string | null > (); + didWarnAboutUseFormState = new Set < string | null > (); } export type Hook = { @@ -235,10 +236,10 @@ type StoreConsistencyCheck = { getSnapshot: () => T, }; -type EventFunctionPayload) => Return> = { +type EventFunctionPayload) => Return > = { ref: { eventFn: F, - impl: F, + impl: F, }, nextImpl: F, }; @@ -337,7 +338,7 @@ function checkDepsAreArrayDev(deps: mixed): void { // It's unlikely their type would change as usually you define them inline. console.error( '%s received a final argument that is not an array (instead, received `%s`). When ' + - 'specified, the final argument must be an array.', + 'specified, the final argument must be an array.', currentHookNameInDev, typeof deps, ); @@ -378,12 +379,12 @@ function warnOnHookMismatchInDev(currentHookName: HookType): void { console.error( 'React has detected a change in the order of Hooks called by %s. ' + - 'This will lead to bugs and errors if not fixed. ' + - 'For more information, read the Rules of Hooks: https://react.dev/link/rules-of-hooks\n\n' + - ' Previous render Next render\n' + - ' ------------------------------------------------------\n' + - '%s' + - ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', + 'This will lead to bugs and errors if not fixed. ' + + 'For more information, read the Rules of Hooks: https://react.dev/link/rules-of-hooks\n\n' + + ' Previous render Next render\n' + + ' ------------------------------------------------------\n' + + '%s' + + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table, ); @@ -400,7 +401,7 @@ function warnOnUseFormStateInDev(): void { console.error( 'ReactDOM.useFormState has been renamed to React.useActionState. ' + - 'Please update %s to use React.useActionState.', + 'Please update %s to use React.useActionState.', componentName, ); } @@ -419,7 +420,7 @@ function warnIfAsyncClientComponent(Component: Function) { Object.prototype.toString.call(Component) === '[object AsyncFunction]' || // $FlowIgnore[method-unbinding] Object.prototype.toString.call(Component) === - '[object AsyncGeneratorFunction]'; + '[object AsyncGeneratorFunction]'; if (isAsyncFunction) { // Encountered an async Client Component. This is not yet supported. const componentName = getComponentNameFromFiber(currentlyRenderingFiber); @@ -427,9 +428,9 @@ function warnIfAsyncClientComponent(Component: Function) { didWarnAboutAsyncClientComponent.add(componentName); console.error( '%s is an async Client Component. ' + - 'Only Server Components can be async at the moment. This error is often caused by accidentally ' + - "adding `'use client'` to a module that was originally written " + - 'for the server.', + 'Only Server Components can be async at the moment. This error is often caused by accidentally ' + + "adding `'use client'` to a module that was originally written " + + 'for the server.', componentName === null ? 'An unknown Component' : `<${componentName}>`, @@ -442,11 +443,11 @@ function warnIfAsyncClientComponent(Component: Function) { function throwInvalidHookError() { throw new Error( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + - ' one of the following reasons:\n' + - '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + - '2. You might be breaking the Rules of Hooks\n' + - '3. You might have more than one copy of React in the same app\n' + - 'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.', + ' one of the following reasons:\n' + + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + + '2. You might be breaking the Rules of Hooks\n' + + '3. You might have more than one copy of React in the same app\n' + + 'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.', ); } @@ -465,8 +466,8 @@ function areHookInputsEqual( if (__DEV__) { console.error( '%s received a final argument during this render, but not during ' + - 'the previous render. Even though the final argument is optional, ' + - 'its type cannot change between renders.', + 'the previous render. Even though the final argument is optional, ' + + 'its type cannot change between renders.', currentHookNameInDev, ); } @@ -479,9 +480,9 @@ function areHookInputsEqual( if (nextDeps.length !== prevDeps.length) { console.error( 'The final argument passed to %s changed size between renders. The ' + - 'order and size of this array must remain constant.\n\n' + - 'Previous: %s\n' + - 'Incoming: %s', + 'order and size of this array must remain constant.\n\n' + + 'Previous: %s\n' + + 'Incoming: %s', currentHookNameInDev, `[${prevDeps.join(', ')}]`, `[${nextDeps.join(', ')}]`, @@ -513,7 +514,7 @@ export function renderWithHooks( if (__DEV__) { hookTypesDev = current !== null - ? ((current._debugHookTypes: any): Array) + ? ((current._debugHookTypes: any): Array < HookType >) : null; hookTypesUpdateIndexDev = -1; // Used for hot reloading: @@ -678,7 +679,7 @@ function finishRenderingHooks( if ( current !== null && (current.flags & StaticMaskEffect) !== - (workInProgress.flags & StaticMaskEffect) && + (workInProgress.flags & StaticMaskEffect) && // Disable this warning in legacy mode, because legacy Suspense is weird // and creates false positives. To make this work in legacy mode, we'd // need to mark fibers that commit in an incomplete state, somehow. For @@ -688,7 +689,7 @@ function finishRenderingHooks( ) { console.error( 'Internal React error: Expected static flag was missing. Please ' + - 'notify the React team.', + 'notify the React team.', ); } } @@ -703,7 +704,7 @@ function finishRenderingHooks( if (didRenderTooFewHooks) { throw new Error( 'Rendered fewer hooks than expected. This may be caused by an accidental ' + - 'early return statement.', + 'early return statement.', ); } @@ -740,8 +741,8 @@ function finishRenderingHooks( didWarnAboutUseWrappedInTryCatch.add(componentName); console.error( '`use` was called from inside a try/catch block. This is not allowed ' + - 'and can lead to unexpected behavior. To handle errors triggered ' + - 'by `use`, wrap your component in a error boundary.', + 'and can lead to unexpected behavior. To handle errors triggered ' + + 'by `use`, wrap your component in a error boundary.', ); } } @@ -817,7 +818,7 @@ function renderWithHooksAgain( if (numberOfReRenders >= RE_RENDER_LIMIT) { throw new Error( 'Too many re-renders. React limits the number of renders to prevent ' + - 'an infinite loop.', + 'an infinite loop.', ); } @@ -1117,8 +1118,8 @@ function useThenable(thenable: Thenable): T { const nextWorkInProgressHook = workInProgressHook === null ? // We're at the beginning of the list, so read from the first hook from - // the fiber. - workInProgressFiber.memoizedState + // the fiber. + workInProgressFiber.memoizedState : workInProgressHook.next; if (nextWorkInProgressHook !== null) { @@ -1207,7 +1208,7 @@ function useMemoCache(size: number): Array { data: enableNoCloningMemoCache ? currentMemoCache.data : // Clone the memo cache before each render (copy-on-write) - currentMemoCache.data.map(array => array.slice()), + currentMemoCache.data.map(array => array.slice()), index: 0 as number, }; } @@ -1238,7 +1239,7 @@ function useMemoCache(size: number): Array { if (__DEV__) { console.error( 'Expected a constant size argument for each invocation of useMemoCache. ' + - 'The previous cache was allocated with size %s but size %s was requested.', + 'The previous cache was allocated with size %s but size %s was requested.', data.length, size, ); @@ -1309,7 +1310,7 @@ function updateReducerImpl( if (queue === null) { throw new Error( 'Should have a queue. You are likely calling Hooks conditionally, ' + - 'which is not allowed. (https://react.dev/link/invalid-hook-call)', + 'which is not allowed. (https://react.dev/link/invalid-hook-call)', ); } @@ -1336,7 +1337,7 @@ function updateReducerImpl( // the future if we implement resuming, or some form of that. console.error( 'Internal error: Expected work-in-progress queue to be a clone. ' + - 'This is a bug in React.', + 'This is a bug in React.', ); } } @@ -1584,7 +1585,7 @@ function rerenderReducer( if (queue === null) { throw new Error( 'Should have a queue. You are likely calling Hooks conditionally, ' + - 'which is not allowed. (https://react.dev/link/invalid-hook-call)', + 'which is not allowed. (https://react.dev/link/invalid-hook-call)', ); } @@ -1632,8 +1633,8 @@ function rerenderReducer( function mountSyncExternalStore( subscribe: (() => void) => () => void, - getSnapshot: () => T, - getServerSnapshot?: () => T, + getSnapshot: () => T, + getServerSnapshot ?: () => T, ): T { const fiber = currentlyRenderingFiber; const hook = mountWorkInProgressHook(); @@ -1644,7 +1645,7 @@ function mountSyncExternalStore( if (getServerSnapshot === undefined) { throw new Error( 'Missing getServerSnapshot, which is required for ' + - 'server-rendered content. Will revert to client rendering.', + 'server-rendered content. Will revert to client rendering.', ); } nextSnapshot = getServerSnapshot(); @@ -1723,8 +1724,8 @@ function mountSyncExternalStore( function updateSyncExternalStore( subscribe: (() => void) => () => void, - getSnapshot: () => T, - getServerSnapshot?: () => T, + getSnapshot: () => T, + getServerSnapshot ?: () => T, ): T { const fiber = currentlyRenderingFiber; const hook = updateWorkInProgressHook(); @@ -1738,7 +1739,7 @@ function updateSyncExternalStore( if (getServerSnapshot === undefined) { throw new Error( 'Missing getServerSnapshot, which is required for ' + - 'server-rendered content. Will revert to client rendering.', + 'server-rendered content. Will revert to client rendering.', ); } nextSnapshot = getServerSnapshot(); @@ -2050,31 +2051,31 @@ type ActionStateQueue = { // If it's null, it means the action queue errored and subsequent actions // should not run. action: ((Awaited, P) => S) | null, - // This is a circular linked list of pending action payloads. It incudes the - // action that is currently running. - pending: ActionStateQueueNode | null, + // This is a circular linked list of pending action payloads. It incudes the + // action that is currently running. + pending: ActionStateQueueNode < S, P > | null, }; type ActionStateQueueNode = { payload: P, // This is the action implementation at the time it was dispatched. action: (Awaited, P) => S, - // This is never null because it's part of a circular linked list. - next: ActionStateQueueNode, - - // Whether or not the action was dispatched as part of a transition. We use - // this to restore the transition context when the queued action is run. Once - // we're able to track parallel async actions, this should be updated to - // represent the specific transition instance the action is associated with. - isTransition: boolean, - - // Implements the Thenable interface. We use it to suspend until the action - // finishes. - then: (listener: () => void) => void, - status: 'pending' | 'rejected' | 'fulfilled', - value: any, - reason: any, - listeners: Array<() => void>, + // This is never null because it's part of a circular linked list. + next: ActionStateQueueNode < S, P >, + + // Whether or not the action was dispatched as part of a transition. We use + // this to restore the transition context when the queued action is run. Once + // we're able to track parallel async actions, this should be updated to + // represent the specific transition instance the action is associated with. + isTransition: boolean, + + // Implements the Thenable interface. We use it to suspend until the action + // finishes. + then: (listener: () => void) => void, + status: 'pending' | 'rejected' | 'fulfilled', + value: any, + reason: any, + listeners: Array < () => void>, }; function dispatchActionState( @@ -2169,11 +2170,11 @@ function runActionStateAction( currentTransition.types = prevTransition !== null ? // If we're a nested transition, we should use the same set as the parent - // since we're conceptually always joined into the same entangled transition. - // In practice, this only matters if we add transition types in the inner - // without setting state. In that case, the inner transition can finish - // without waiting for the outer. - prevTransition.types + // since we're conceptually always joined into the same entangled transition. + // In practice, this only matters if we add transition types in the inner + // without setting state. In that case, the inner transition can finish + // without waiting for the outer. + prevTransition.types : null; } if (enableGestureTransition) { @@ -2208,8 +2209,8 @@ function runActionStateAction( // Just assert that assumption holds that we're not overriding anything. console.error( 'We expected inner Transitions to have transferred the outer types set and ' + - 'that you cannot add to the outer Transition while inside the inner.' + - 'This is a bug in React.', + 'that you cannot add to the outer Transition while inside the inner.' + + 'This is a bug in React.', ); } } @@ -2224,8 +2225,8 @@ function runActionStateAction( if (updatedFibersCount > 10) { console.warn( 'Detected a large number of updates inside startTransition. ' + - 'If this is due to a subscription please re-write it to use React provided hooks. ' + - 'Otherwise concurrent mode guarantees are off the table.', + 'If this is due to a subscription please re-write it to use React provided hooks. ' + + 'Otherwise concurrent mode guarantees are off the table.', ); } } @@ -2272,9 +2273,9 @@ function handleActionReturnValue( if (!node.isTransition) { console.error( 'An async function with useActionState was called outside of a transition. ' + - 'This is likely not what you intended (for example, isPending will not update ' + - 'correctly). Either call the returned function inside startTransition, or pass it ' + - 'to an `action` or `formAction` prop.', + 'This is likely not what you intended (for example, isPending will not update ' + + 'correctly). Either call the returned function inside startTransition, or pass it ' + + 'to an `action` or `formAction` prop.', ); } } @@ -2354,9 +2355,9 @@ function actionStateReducer(oldState: S, newState: S): S { function mountActionState( action: (Awaited, P) => S, - initialStateProp: Awaited, - permalink?: string, -): [Awaited, (P) => void, boolean] { + initialStateProp: Awaited < S >, + permalink ?: string, +): [Awaited < S >, (P) => void, boolean] { let initialState: Awaited = initialStateProp; if (getIsHydrating()) { const root: FiberRoot = (getWorkInProgressRoot(): any); @@ -2397,14 +2398,14 @@ function mountActionState( // Pending state. This is used to store the pending state of the action. // Tracked optimistically, like a transition pending state. - const pendingStateHook = mountStateImpl((false: Thenable | boolean)); + const pendingStateHook = mountStateImpl((false: Thenable < boolean > | boolean)); const setPendingState: boolean => void = (dispatchOptimisticSetState.bind( null, currentlyRenderingFiber, false, - ((pendingStateHook.queue: any): UpdateQueue< - S | Awaited, - S | Awaited, + ((pendingStateHook.queue: any): UpdateQueue < + S | Awaited < S >, + S | Awaited < S >, >), ): any); @@ -2439,9 +2440,9 @@ function mountActionState( function updateActionState( action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, -): [Awaited, (P) => void, boolean] { + initialState: Awaited < S >, + permalink ?: string, +): [Awaited < S >, (P) => void, boolean] { const stateHook = updateWorkInProgressHook(); const currentStateHook = ((currentHook: any): Hook); return updateActionStateImpl( @@ -2457,10 +2458,10 @@ function updateActionStateImpl( stateHook: Hook, currentStateHook: Hook, action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, -): [Awaited, (P) => void, boolean] { - const [actionResult] = updateReducerImpl, S | Thenable>( + initialState: Awaited < S >, + permalink ?: string, +): [Awaited < S >, (P) => void, boolean] { + const [actionResult] = updateReducerImpl < S | Thenable < S >, S | Thenable < S >> ( stateHook, currentStateHook, actionStateReducer, @@ -2519,9 +2520,9 @@ function actionStateActionEffect( function rerenderActionState( action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, -): [Awaited, (P) => void, boolean] { + initialState: Awaited < S >, + permalink ?: string, +): [Awaited < S >, (P) => void, boolean] { // Unlike useState, useActionState doesn't support render phase updates. // Also unlike useState, we need to replay all pending updates again in case // the passthrough value changed. @@ -2596,17 +2597,17 @@ function pushEffectImpl(effect: Effect): Effect { } function createEffectInstance(): EffectInstance { - return {destroy: undefined}; + return { destroy: undefined }; } -function mountRef(initialValue: T): {current: T} { +function mountRef(initialValue: T): { current: T } { const hook = mountWorkInProgressHook(); - const ref = {current: initialValue}; + const ref = { current: initialValue }; hook.memoizedState = ref; return ref; } -function updateRef(initialValue: T): {current: T} { +function updateRef(initialValue: T): { current: T } { const hook = updateWorkInProgressHook(); return hook.memoizedState; } @@ -2699,8 +2700,8 @@ function updateEffect( updateEffectImpl(PassiveEffect, HookPassive, create, deps); } -function useEffectEventImpl) => Return>( - payload: EventFunctionPayload, +function useEffectEventImpl) => Return > ( + payload: EventFunctionPayload < Args, Return, F >, ) { currentlyRenderingFiber.flags |= UpdateEffect; let componentUpdateQueue: null | FunctionComponentUpdateQueue = @@ -2719,11 +2720,11 @@ function useEffectEventImpl) => Return>( } } -function mountEvent) => Return>( +function mountEvent) => Return > ( callback: F, ): F { const hook = mountWorkInProgressHook(); - const ref = {impl: callback}; + const ref = { impl: callback }; hook.memoizedState = ref; // $FlowIgnore[incompatible-return] return function eventFn() { @@ -2736,12 +2737,12 @@ function mountEvent) => Return>( }; } -function updateEvent) => Return>( +function updateEvent) => Return > ( callback: F, ): F { const hook = updateWorkInProgressHook(); const ref = hook.memoizedState; - useEffectEventImpl({ref, nextImpl: callback}); + useEffectEventImpl({ ref, nextImpl: callback }); // $FlowIgnore[incompatible-return] return function eventFn() { if (isInvalidExecutionContextForEventFunction()) { @@ -2790,7 +2791,7 @@ function updateLayoutEffect( function imperativeHandleEffect( create: () => T, - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, ): void | (() => void) { if (typeof ref === 'function') { const refCallback = ref; @@ -2810,7 +2811,7 @@ function imperativeHandleEffect( if (!refObject.hasOwnProperty('current')) { console.error( 'Expected useImperativeHandle() first argument to either be a ' + - 'ref callback or React.createRef() object. Instead received: %s.', + 'ref callback or React.createRef() object. Instead received: %s.', 'an object with keys {' + Object.keys(refObject).join(', ') + '}', ); } @@ -2824,7 +2825,7 @@ function imperativeHandleEffect( } function mountImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, create: () => T, deps: Array | void | null, ): void { @@ -2832,7 +2833,7 @@ function mountImperativeHandle( if (typeof create !== 'function') { console.error( 'Expected useImperativeHandle() second argument to be a function ' + - 'that creates a handle. Instead received: %s.', + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null', ); } @@ -2858,7 +2859,7 @@ function mountImperativeHandle( } function updateImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, create: () => T, deps: Array | void | null, ): void { @@ -2866,7 +2867,7 @@ function updateImperativeHandle( if (typeof create !== 'function') { console.error( 'Expected useImperativeHandle() second argument to be a function ' + - 'that creates a handle. Instead received: %s.', + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null', ); } @@ -3105,11 +3106,11 @@ function startTransition( currentTransition.types = prevTransition !== null ? // If we're a nested transition, we should use the same set as the parent - // since we're conceptually always joined into the same entangled transition. - // In practice, this only matters if we add transition types in the inner - // without setting state. In that case, the inner transition can finish - // without waiting for the outer. - prevTransition.types + // since we're conceptually always joined into the same entangled transition. + // In practice, this only matters if we add transition types in the inner + // without setting state. In that case, the inner transition can finish + // without waiting for the outer. + prevTransition.types : null; } if (enableGestureTransition) { @@ -3184,7 +3185,7 @@ function startTransition( // When it unwraps the thenable with the `use` algorithm, the error // will be thrown. const rejectedThenable: RejectedThenable = { - then() {}, + then() { }, status: 'rejected', reason: error, }; @@ -3208,8 +3209,8 @@ function startTransition( // Just assert that assumption holds that we're not overriding anything. console.error( 'We expected inner Transitions to have transferred the outer types set and ' + - 'that you cannot add to the outer Transition while inside the inner.' + - 'This is a bug in React.', + 'that you cannot add to the outer Transition while inside the inner.' + + 'This is a bug in React.', ); } } @@ -3224,8 +3225,8 @@ function startTransition( if (updatedFibersCount > 10) { console.warn( 'Detected a large number of updates inside startTransition. ' + - 'If this is due to a subscription please re-write it to use React provided hooks. ' + - 'Otherwise concurrent mode guarantees are off the table.', + 'If this is due to a subscription please re-write it to use React provided hooks. ' + + 'Otherwise concurrent mode guarantees are off the table.', ); } } @@ -3233,51 +3234,51 @@ function startTransition( } } -const noop = () => {}; +const noop = () => { }; export function startHostTransition( formFiber: Fiber, pendingState: TransitionStatus, action: (F => mixed) | null, - formData: F, + formData: F, ): void { if (formFiber.tag !== HostComponent) { - throw new Error( - 'Expected the form instance to be a HostComponent. This ' + - 'is a bug in React.', - ); - } - - const stateHook = ensureFormComponentIsStateful(formFiber); - - const queue: UpdateQueue< - Thenable | TransitionStatus, - BasicStateAction | TransitionStatus>, - > = stateHook.queue; - - startHostActionTimer(formFiber); - - startTransition( - formFiber, - queue, - pendingState, - NoPendingHostTransition, - // TODO: `startTransition` both sets the pending state and dispatches - // the action, if one is provided. Consider refactoring these two - // concerns to avoid the extra lambda. - - action === null - ? // No action was provided, but we still call `startTransition` to - // set the pending form status. - noop - : () => { - // Automatically reset the form when the action completes. - requestFormReset(formFiber); - return action(formData); - }, + throw new Error( + 'Expected the form instance to be a HostComponent. This ' + + 'is a bug in React.', ); } +const stateHook = ensureFormComponentIsStateful(formFiber); + +const queue: UpdateQueue< + Thenable | TransitionStatus, + BasicStateAction | TransitionStatus>, +> = stateHook.queue; + +startHostActionTimer(formFiber, isAlreadyRendering()); + +startTransition( + formFiber, + queue, + pendingState, + NoPendingHostTransition, + // TODO: `startTransition` both sets the pending state and dispatches + // the action, if one is provided. Consider refactoring these two + // concerns to avoid the extra lambda. + + action === null + ? // No action was provided, but we still call `startTransition` to + // set the pending form status. + noop + : () => { + // Automatically reset the form when the action completes. + requestFormReset(formFiber); + return action(formData); + }, +); +} + function ensureFormComponentIsStateful(formFiber: Fiber) { const existingStateHook: Hook | null = formFiber.memoizedState; if (existingStateHook !== null) { @@ -3362,15 +3363,15 @@ export function requestFormReset(formFiber: Fiber) { // described above. But arguably we shouldn't. console.error( 'requestFormReset was called outside a transition or action. To ' + - 'fix, move to an action, or wrap with startTransition.', + 'fix, move to an action, or wrap with startTransition.', ); } } else if (enableGestureTransition && transition.gesture) { throw new Error( 'Cannot requestFormReset() inside a startGestureTransition. ' + - 'There should be no side-effects associated with starting a ' + - 'Gesture until its Action is invoked. Move side-effects to the ' + - 'Action instead.', + 'There should be no side-effects associated with starting a ' + + 'Gesture until its Action is invoked. Move side-effects to the ' + + 'Action instead.', ); } @@ -3398,7 +3399,7 @@ function mountTransition(): [ boolean, (callback: () => void, options?: StartTransitionOptions) => void, ] { - const stateHook = mountStateImpl((false: Thenable | boolean)); + const stateHook = mountStateImpl((false: Thenable < boolean > | boolean)); // The `start` method never changes. const start = startTransition.bind( null, @@ -3423,7 +3424,7 @@ function updateTransition(): [ typeof booleanOrThenable === 'boolean' ? booleanOrThenable : // This will suspend until the async action scope has finished. - useThenable(booleanOrThenable); + useThenable(booleanOrThenable); return [isPending, start]; } @@ -3438,7 +3439,7 @@ function rerenderTransition(): [ typeof booleanOrThenable === 'boolean' ? booleanOrThenable : // This will suspend until the async action scope has finished. - useThenable(booleanOrThenable); + useThenable(booleanOrThenable); return [isPending, start]; } @@ -3517,7 +3518,12 @@ function refreshCache(fiber: Fiber, seedKey: ?() => T, seedValue: T): void { const refreshUpdate = createLegacyQueueUpdate(lane); const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane); if (root !== null) { - startUpdateTimerByLane(lane, 'refresh()', fiber); + startUpdateTimerByLane( + lane, + 'refresh()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, provider, lane); entangleLegacyQueueTransitions(root, provider, lane); } @@ -3563,8 +3569,8 @@ function dispatchReducerAction( if (typeof args[3] === 'function') { console.error( "State updates from the useState() and useReducer() Hooks don't support the " + - 'second callback argument. To execute a side effect after ' + - 'rendering, declare it in the component body with useEffect().', + 'second callback argument. To execute a side effect after ' + + 'rendering, declare it in the component body with useEffect().', ); } } @@ -3586,7 +3592,12 @@ function dispatchReducerAction( } else { const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); if (root !== null) { - startUpdateTimerByLane(lane, 'dispatch()', fiber); + startUpdateTimerByLane( + lane, + 'dispatch()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, fiber, lane); entangleTransitionUpdate(root, queue, lane); } @@ -3606,8 +3617,8 @@ function dispatchSetState( if (typeof args[3] === 'function') { console.error( "State updates from the useState() and useReducer() Hooks don't support the " + - 'second callback argument. To execute a side effect after ' + - 'rendering, declare it in the component body with useEffect().', + 'second callback argument. To execute a side effect after ' + + 'rendering, declare it in the component body with useEffect().', ); } } @@ -3620,7 +3631,7 @@ function dispatchSetState( lane, ); if (didScheduleUpdate) { - startUpdateTimerByLane(lane, 'setState()', fiber); + startUpdateTimerByLane(lane, 'setState()', fiber, isAlreadyRendering()); } markUpdateInDevTools(fiber, lane, action); } @@ -3735,8 +3746,8 @@ function dispatchOptimisticSetState( // inside a regular event handler (e.g. onSubmit) instead of an action. console.error( 'An optimistic state update occurred outside a transition or ' + - 'action. To fix, move the update to an action, or wrap ' + - 'with startTransition.', + 'action. To fix, move the update to an action, or wrap ' + + 'with startTransition.', ); } } @@ -3782,7 +3793,12 @@ function dispatchOptimisticSetState( // will never be attempted before the optimistic update. This currently // holds because the optimistic update is always synchronous. If we ever // change that, we'll need to account for this. - startUpdateTimerByLane(lane, 'setOptimistic()', fiber); + startUpdateTimerByLane( + lane, + 'setOptimistic()', + fiber, + isAlreadyRendering(), + ); scheduleUpdateOnFiber(root, fiber, lane); // Optimistic updates are always synchronous, so we don't need to call // entangleTransitionUpdate here. @@ -3991,18 +4007,18 @@ if (__DEV__) { const warnInvalidContextAccess = () => { console.error( 'Context can only be read while React is rendering. ' + - 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + - 'In function components, you can read it directly in the function body, but not ' + - 'inside Hooks like useReducer() or useMemo().', + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + + 'In function components, you can read it directly in the function body, but not ' + + 'inside Hooks like useReducer() or useMemo().', ); }; const warnInvalidHookAccess = () => { console.error( 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + - 'You can only call Hooks at the top level of your React function. ' + - 'For more information, see ' + - 'https://react.dev/link/rules-of-hooks', + 'You can only call Hooks at the top level of your React function. ' + + 'For more information, see ' + + 'https://react.dev/link/rules-of-hooks', ); }; @@ -4032,7 +4048,7 @@ if (__DEV__) { return mountEffect(create, deps); }, useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, create: () => T, deps: Array | void | null, ): void { @@ -4086,7 +4102,7 @@ if (__DEV__) { ReactSharedInternals.H = prevDispatcher; } }, - useRef(initialValue: T): {current: T} { + useRef(initialValue: T): { current: T } { currentHookNameInDev = 'useRef'; mountHookTypesDev(); return mountRef(initialValue); @@ -4121,1117 +4137,1117 @@ if (__DEV__) { }, useSyncExternalStore( subscribe: (() => void) => () => void, - getSnapshot: () => T, - getServerSnapshot?: () => T, + getSnapshot: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - mountHookTypesDev(); - return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - mountHookTypesDev(); - return mountId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - mountHookTypesDev(); - warnOnUseFormStateInDev(); - return mountActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - mountHookTypesDev(); - return mountActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, + currentHookNameInDev = 'useSyncExternalStore'; + mountHookTypesDev(); + return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); + }, + useId(): string { + currentHookNameInDev = 'useId'; + mountHookTypesDev(); + return mountId(); + }, + useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + mountHookTypesDev(); + warnOnUseFormStateInDev(); + return mountActionState(action, initialState, permalink); + }, + useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + mountHookTypesDev(); + return mountActionState(action, initialState, permalink); + }, + useOptimistic < S, A > ( + passthrough: S, reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - mountHookTypesDev(); - return mountOptimistic(passthrough, reducer); - }, - useHostTransitionStatus, + currentHookNameInDev = 'useOptimistic'; + mountHookTypesDev(); + return mountOptimistic(passthrough, reducer); + }, + useHostTransitionStatus, useMemoCache, useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - mountHookTypesDev(); - return mountRefresh(); - }, - useEffectEvent) => Return>( - callback: F, + currentHookNameInDev = 'useCacheRefresh'; + mountHookTypesDev(); + return mountRefresh(); + }, + useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - mountHookTypesDev(); - return mountEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + mountHookTypesDev(); + return mountEvent(callback); + }, +}; + +HooksDispatcherOnMountWithHookTypesInDEV = { + readContext(context: ReactContext): T { + return readContext(context); + }, + use, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + updateHookTypesDev(); + return mountCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + updateHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + updateHookTypesDev(); + return mountEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + updateHookTypesDev(); + return mountImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + updateHookTypesDev(); + return mountInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + updateHookTypesDev(); + return mountLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + updateHookTypesDev(); + return mountRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + updateHookTypesDev(); + return mountDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + updateHookTypesDev(); + return mountDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + updateHookTypesDev(); + return mountTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot ?: () => T, + ): T { + currentHookNameInDev = 'useSyncExternalStore'; + updateHookTypesDev(); + return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + updateHookTypesDev(); + return mountId(); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + updateHookTypesDev(); + return mountActionState(action, initialState, permalink); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + updateHookTypesDev(); + warnOnUseFormStateInDev(); + return mountActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, + ): [S, (A) => void] { + currentHookNameInDev = 'useOptimistic'; + updateHookTypesDev(); + return mountOptimistic(passthrough, reducer); +}, +useHostTransitionStatus, + useMemoCache, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + updateHookTypesDev(); + return mountRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, + ): F { + currentHookNameInDev = 'useEffectEvent'; + updateHookTypesDev(); + return mountEvent(callback); +}, }; - HooksDispatcherOnMountWithHookTypesInDEV = { - readContext(context: ReactContext): T { - return readContext(context); - }, - use, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - updateHookTypesDev(); - return mountCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - updateHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - updateHookTypesDev(); - return mountEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - updateHookTypesDev(); - return mountImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - updateHookTypesDev(); - return mountInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - updateHookTypesDev(); - return mountLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - updateHookTypesDev(); - return mountRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - updateHookTypesDev(); - return mountDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - updateHookTypesDev(); - return mountDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - updateHookTypesDev(); - return mountTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, +HooksDispatcherOnUpdateInDEV = { + readContext(context: ReactContext): T { + return readContext(context); + }, + use, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + updateHookTypesDev(); + return updateCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + updateHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + updateHookTypesDev(); + return updateEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + updateHookTypesDev(); + return updateImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + updateHookTypesDev(); + return updateInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + updateHookTypesDev(); + return updateLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + updateHookTypesDev(); + return updateRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + updateHookTypesDev(); + return updateDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + updateHookTypesDev(); + return updateDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + updateHookTypesDev(); + return updateTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, getSnapshot: () => T, - getServerSnapshot?: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - updateHookTypesDev(); - return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - updateHookTypesDev(); - return mountId(); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - updateHookTypesDev(); - return mountActionState(action, initialState, permalink); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - updateHookTypesDev(); - warnOnUseFormStateInDev(); - return mountActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, + currentHookNameInDev = 'useSyncExternalStore'; + updateHookTypesDev(); + return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + updateHookTypesDev(); + return updateId(); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + updateHookTypesDev(); + warnOnUseFormStateInDev(); + return updateActionState(action, initialState, permalink); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + updateHookTypesDev(); + return updateActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - updateHookTypesDev(); - return mountOptimistic(passthrough, reducer); - }, - useHostTransitionStatus, - useMemoCache, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return mountRefresh(); - }, - useEffectEvent) => Return>( - callback: F, + currentHookNameInDev = 'useOptimistic'; + updateHookTypesDev(); + return updateOptimistic(passthrough, reducer); +}, +useHostTransitionStatus, + useMemoCache, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + updateHookTypesDev(); + return updateRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - updateHookTypesDev(); - return mountEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + updateHookTypesDev(); + return updateEvent(callback); +}, }; - HooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { - return readContext(context); - }, - use, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - updateHookTypesDev(); - return updateCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - updateHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - updateHookTypesDev(); - return updateEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - updateHookTypesDev(); - return updateImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - updateHookTypesDev(); - return updateInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - updateHookTypesDev(); - return updateLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - updateHookTypesDev(); - return updateRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - updateHookTypesDev(); - return updateDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - updateHookTypesDev(); - return updateDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - updateHookTypesDev(); - return updateTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, - getSnapshot: () => T, - getServerSnapshot?: () => T, - ): T { - currentHookNameInDev = 'useSyncExternalStore'; - updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - updateHookTypesDev(); - return updateId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - updateHookTypesDev(); - warnOnUseFormStateInDev(); - return updateActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - updateHookTypesDev(); - return updateActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, - ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - updateHookTypesDev(); - return updateOptimistic(passthrough, reducer); - }, - useHostTransitionStatus, - useMemoCache, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return updateRefresh(); - }, - useEffectEvent) => Return>( - callback: F, - ): F { - currentHookNameInDev = 'useEffectEvent'; - updateHookTypesDev(); - return updateEvent(callback); - }, - }; - - HooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { - return readContext(context); - }, - use, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - updateHookTypesDev(); - return updateCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - updateHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - updateHookTypesDev(); - return updateEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - updateHookTypesDev(); - return updateImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - updateHookTypesDev(); - return updateInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - updateHookTypesDev(); - return updateLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; - try { - return updateMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; - try { - return rerenderReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - updateHookTypesDev(); - return updateRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; - try { - return rerenderState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - updateHookTypesDev(); - return updateDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - updateHookTypesDev(); - return rerenderDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - updateHookTypesDev(); - return rerenderTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, +HooksDispatcherOnRerenderInDEV = { + readContext(context: ReactContext): T { + return readContext(context); + }, + use, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + updateHookTypesDev(); + return updateCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + updateHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + updateHookTypesDev(); + return updateEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + updateHookTypesDev(); + return updateImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + updateHookTypesDev(); + return updateInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + updateHookTypesDev(); + return updateLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; + try { + return updateMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; + try { + return rerenderReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + updateHookTypesDev(); + return updateRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV; + try { + return rerenderState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + updateHookTypesDev(); + return updateDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + updateHookTypesDev(); + return rerenderDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + updateHookTypesDev(); + return rerenderTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, getSnapshot: () => T, - getServerSnapshot?: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - updateHookTypesDev(); - return updateId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - updateHookTypesDev(); - warnOnUseFormStateInDev(); - return rerenderActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - updateHookTypesDev(); - return rerenderActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, + currentHookNameInDev = 'useSyncExternalStore'; + updateHookTypesDev(); + return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + updateHookTypesDev(); + return updateId(); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + updateHookTypesDev(); + warnOnUseFormStateInDev(); + return rerenderActionState(action, initialState, permalink); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + updateHookTypesDev(); + return rerenderActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - updateHookTypesDev(); - return rerenderOptimistic(passthrough, reducer); - }, - useHostTransitionStatus, - useMemoCache, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return updateRefresh(); - }, - useEffectEvent) => Return>( - callback: F, + currentHookNameInDev = 'useOptimistic'; + updateHookTypesDev(); + return rerenderOptimistic(passthrough, reducer); +}, +useHostTransitionStatus, + useMemoCache, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + updateHookTypesDev(); + return updateRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - updateHookTypesDev(); - return updateEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + updateHookTypesDev(); + return updateEvent(callback); +}, }; - InvalidNestedHooksDispatcherOnMountInDEV = { - readContext(context: ReactContext): T { - warnInvalidContextAccess(); - return readContext(context); - }, - use(usable: Usable): T { - warnInvalidHookAccess(); - return use(usable); - }, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - warnInvalidHookAccess(); - mountHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - warnInvalidHookAccess(); - mountHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - warnInvalidHookAccess(); - mountHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; - try { - return mountState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, +InvalidNestedHooksDispatcherOnMountInDEV = { + readContext(context: ReactContext): T { + warnInvalidContextAccess(); + return readContext(context); + }, + use(usable: Usable): T { + warnInvalidHookAccess(); + return use(usable); + }, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + warnInvalidHookAccess(); + mountHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + warnInvalidHookAccess(); + mountHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + warnInvalidHookAccess(); + mountHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; + try { + return mountState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, getSnapshot: () => T, - getServerSnapshot?: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, + currentHookNameInDev = 'useSyncExternalStore'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountId(); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountActionState(action, initialState, permalink); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountOptimistic(passthrough, reducer); - }, - useMemoCache(size: number): Array { - warnInvalidHookAccess(); + currentHookNameInDev = 'useOptimistic'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountOptimistic(passthrough, reducer); +}, +useMemoCache(size: number): Array < any > { + warnInvalidHookAccess(); return useMemoCache(size); - }, - useHostTransitionStatus, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - mountHookTypesDev(); - return mountRefresh(); - }, - useEffectEvent) => Return>( - callback: F, +}, + useHostTransitionStatus, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + mountHookTypesDev(); + return mountRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + warnInvalidHookAccess(); + mountHookTypesDev(); + return mountEvent(callback); +}, }; - InvalidNestedHooksDispatcherOnUpdateInDEV = { - readContext(context: ReactContext): T { - warnInvalidContextAccess(); - return readContext(context); - }, - use(usable: Usable): T { - warnInvalidHookAccess(); - return use(usable); - }, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, +InvalidNestedHooksDispatcherOnUpdateInDEV = { + readContext(context: ReactContext): T { + warnInvalidContextAccess(); + return readContext(context); + }, + use(usable: Usable): T { + warnInvalidHookAccess(); + return use(usable); + }, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, getSnapshot: () => T, - getServerSnapshot?: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, + currentHookNameInDev = 'useSyncExternalStore'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateId(); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateActionState(action, initialState, permalink); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateOptimistic(passthrough, reducer); - }, - useMemoCache(size: number): Array { - warnInvalidHookAccess(); + currentHookNameInDev = 'useOptimistic'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateOptimistic(passthrough, reducer); +}, +useMemoCache(size: number): Array < any > { + warnInvalidHookAccess(); return useMemoCache(size); - }, - useHostTransitionStatus, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return updateRefresh(); - }, - useEffectEvent) => Return>( - callback: F, +}, + useHostTransitionStatus, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + updateHookTypesDev(); + return updateRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateEvent(callback); +}, }; - InvalidNestedHooksDispatcherOnRerenderInDEV = { - readContext(context: ReactContext): T { - warnInvalidContextAccess(); - return readContext(context); - }, - use(usable: Usable): T { - warnInvalidHookAccess(); - return use(usable); - }, - useCallback(callback: T, deps: Array | void | null): T { - currentHookNameInDev = 'useCallback'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateCallback(callback, deps); - }, - useContext(context: ReactContext): T { - currentHookNameInDev = 'useContext'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return readContext(context); - }, - useEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEffect(create, deps); - }, - useImperativeHandle( - ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, - create: () => T, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useImperativeHandle'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateImperativeHandle(ref, create, deps); - }, - useInsertionEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useInsertionEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateInsertionEffect(create, deps); - }, - useLayoutEffect( - create: () => (() => void) | void, - deps: Array | void | null, - ): void { - currentHookNameInDev = 'useLayoutEffect'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateLayoutEffect(create, deps); - }, - useMemo(create: () => T, deps: Array | void | null): T { - currentHookNameInDev = 'useMemo'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return updateMemo(create, deps); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useReducer( - reducer: (S, A) => S, - initialArg: I, - init?: I => S, - ): [S, Dispatch] { - currentHookNameInDev = 'useReducer'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return rerenderReducer(reducer, initialArg, init); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useRef(initialValue: T): {current: T} { - currentHookNameInDev = 'useRef'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateRef(initialValue); - }, - useState( - initialState: (() => S) | S, - ): [S, Dispatch>] { - currentHookNameInDev = 'useState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - const prevDispatcher = ReactSharedInternals.H; - ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; - try { - return rerenderState(initialState); - } finally { - ReactSharedInternals.H = prevDispatcher; - } - }, - useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { - currentHookNameInDev = 'useDebugValue'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateDebugValue(value, formatterFn); - }, - useDeferredValue(value: T, initialValue?: T): T { - currentHookNameInDev = 'useDeferredValue'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderDeferredValue(value, initialValue); - }, - useTransition(): [boolean, (() => void) => void] { - currentHookNameInDev = 'useTransition'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderTransition(); - }, - useSyncExternalStore( - subscribe: (() => void) => () => void, +InvalidNestedHooksDispatcherOnRerenderInDEV = { + readContext(context: ReactContext): T { + warnInvalidContextAccess(); + return readContext(context); + }, + use(usable: Usable): T { + warnInvalidHookAccess(); + return use(usable); + }, + useCallback(callback: T, deps: Array | void | null): T { + currentHookNameInDev = 'useCallback'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateCallback(callback, deps); + }, + useContext(context: ReactContext): T { + currentHookNameInDev = 'useContext'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return readContext(context); + }, + useEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateEffect(create, deps); + }, + useImperativeHandle( + ref: { current: T | null } | ((inst: T | null) => mixed) | null | void, + create: () => T, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useImperativeHandle'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateImperativeHandle(ref, create, deps); + }, + useInsertionEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useInsertionEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateInsertionEffect(create, deps); + }, + useLayoutEffect( + create: () => (() => void) | void, + deps: Array | void | null, + ): void { + currentHookNameInDev = 'useLayoutEffect'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateLayoutEffect(create, deps); + }, + useMemo(create: () => T, deps: Array | void | null): T { + currentHookNameInDev = 'useMemo'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return updateMemo(create, deps); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useReducer( + reducer: (S, A) => S, + initialArg: I, + init?: I => S, + ): [S, Dispatch] { + currentHookNameInDev = 'useReducer'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return rerenderReducer(reducer, initialArg, init); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useRef(initialValue: T): { current: T } { + currentHookNameInDev = 'useRef'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateRef(initialValue); + }, + useState( + initialState: (() => S) | S, + ): [S, Dispatch>] { + currentHookNameInDev = 'useState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + const prevDispatcher = ReactSharedInternals.H; + ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV; + try { + return rerenderState(initialState); + } finally { + ReactSharedInternals.H = prevDispatcher; + } + }, + useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void { + currentHookNameInDev = 'useDebugValue'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateDebugValue(value, formatterFn); + }, + useDeferredValue(value: T, initialValue?: T): T { + currentHookNameInDev = 'useDeferredValue'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return rerenderDeferredValue(value, initialValue); + }, + useTransition(): [boolean, (() => void) => void] { + currentHookNameInDev = 'useTransition'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return rerenderTransition(); + }, + useSyncExternalStore( + subscribe: (() => void) => () => void, getSnapshot: () => T, - getServerSnapshot?: () => T, + getServerSnapshot ?: () => T, ): T { - currentHookNameInDev = 'useSyncExternalStore'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - }, - useId(): string { - currentHookNameInDev = 'useId'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateId(); - }, - useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useFormState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderActionState(action, initialState, permalink); - }, - useActionState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, - ): [Awaited, (P) => void, boolean] { - currentHookNameInDev = 'useActionState'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderActionState(action, initialState, permalink); - }, - useOptimistic( - passthrough: S, - reducer: ?(S, A) => S, + currentHookNameInDev = 'useSyncExternalStore'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); +}, +useId(): string { + currentHookNameInDev = 'useId'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateId(); +}, +useFormState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useFormState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return rerenderActionState(action, initialState, permalink); +}, +useActionState < S, P > ( + action: (Awaited < S >, P) => S, + initialState: Awaited < S >, + permalink ?: string, + ): [Awaited < S >, (P) => void, boolean] { + currentHookNameInDev = 'useActionState'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return rerenderActionState(action, initialState, permalink); +}, +useOptimistic < S, A > ( + passthrough: S, + reducer: ?(S, A) => S, ): [S, (A) => void] { - currentHookNameInDev = 'useOptimistic'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderOptimistic(passthrough, reducer); - }, - useMemoCache(size: number): Array { - warnInvalidHookAccess(); + currentHookNameInDev = 'useOptimistic'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return rerenderOptimistic(passthrough, reducer); +}, +useMemoCache(size: number): Array < any > { + warnInvalidHookAccess(); return useMemoCache(size); - }, - useHostTransitionStatus, - useCacheRefresh() { - currentHookNameInDev = 'useCacheRefresh'; - updateHookTypesDev(); - return updateRefresh(); - }, - useEffectEvent) => Return>( - callback: F, +}, + useHostTransitionStatus, + useCacheRefresh() { + currentHookNameInDev = 'useCacheRefresh'; + updateHookTypesDev(); + return updateRefresh(); +}, +useEffectEvent < Args, Return, F: (...Array) => Return > ( + callback: F, ): F { - currentHookNameInDev = 'useEffectEvent'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateEvent(callback); - }, + currentHookNameInDev = 'useEffectEvent'; + warnInvalidHookAccess(); + updateHookTypesDev(); + return updateEvent(callback); +}, }; } diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js index 4a36b39c43c8..a9b04f72ab16 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.js @@ -13,23 +13,23 @@ import type { SuspenseHydrationCallbacks, TransitionTracingCallbacks, } from './ReactInternalTypes'; -import type {RootTag} from './ReactRootTags'; +import type { RootTag } from './ReactRootTags'; import type { Container, PublicInstance, RendererInspectionConfig, } from './ReactFiberConfig'; -import type {ReactNodeList, ReactFormState} from 'shared/ReactTypes'; -import type {Lane} from './ReactFiberLane'; -import type {ActivityState} from './ReactFiberActivityComponent'; -import type {SuspenseState} from './ReactFiberSuspenseComponent'; +import type { ReactNodeList, ReactFormState } from 'shared/ReactTypes'; +import type { Lane } from './ReactFiberLane'; +import type { ActivityState } from './ReactFiberActivityComponent'; +import type { SuspenseState } from './ReactFiberSuspenseComponent'; -import {LegacyRoot} from './ReactRootTags'; +import { LegacyRoot } from './ReactRootTags'; import { findCurrentHostFiber, findCurrentHostFiberWithNoPortals, } from './ReactFiberTreeReflection'; -import {get as getInstance} from 'shared/ReactInstanceMap'; +import { get as getInstance } from 'shared/ReactInstanceMap'; import { HostComponent, HostSingleton, @@ -57,15 +57,15 @@ import { emptyContextObject, isContextProvider as isLegacyContextProvider, } from './ReactFiberLegacyContext'; -import {createFiberRoot} from './ReactFiberRoot'; -import {isRootDehydrated} from './ReactFiberShellHydration'; +import { createFiberRoot } from './ReactFiberRoot'; +import { isRootDehydrated } from './ReactFiberShellHydration'; import { injectInternals, markRenderScheduled, onScheduleRoot, injectProfilingHooks, } from './ReactFiberDevToolsHook'; -import {startUpdateTimerByLane} from './ReactProfilerTimer'; +import { startUpdateTimerByLane } from './ReactProfilerTimer'; import { requestUpdateLane, scheduleUpdateOnFiber, @@ -79,7 +79,7 @@ import { discreteUpdates, flushPendingEffects, } from './ReactFiberWorkLoop'; -import {enqueueConcurrentRenderForLane} from './ReactFiberConcurrentUpdates'; +import { enqueueConcurrentRenderForLane } from './ReactFiberConcurrentUpdates'; import { createUpdate, enqueueUpdate, @@ -90,7 +90,7 @@ import { current as ReactCurrentFiberCurrent, runWithFiberInDEV, } from './ReactCurrentFiber'; -import {StrictLegacyMode} from './ReactTypeOfMode'; +import { StrictLegacyMode } from './ReactTypeOfMode'; import { SyncLane, SelectiveHydrationLane, @@ -105,7 +105,7 @@ import { setRefreshHandler, } from './ReactFiberHotReloading'; import ReactVersion from 'shared/ReactVersion'; -export {createPortal} from './ReactPortal'; +export { createPortal } from './ReactPortal'; export { createComponentSelector, createHasPseudoClassSelector, @@ -118,14 +118,14 @@ export { focusWithin, observeVisibleRects, } from './ReactTestSelectors'; -export {startHostTransition} from './ReactFiberHooks'; +export { startHostTransition } from './ReactFiberHooks'; export { defaultOnUncaughtError, defaultOnCaughtError, defaultOnRecoverableError, } from './ReactFiberErrorLogger'; -import {getLabelForLane, TotalLanes} from 'react-reconciler/src/ReactFiberLane'; -import {registerDefaultIndicator} from './ReactFiberAsyncAction'; +import { getLabelForLane, TotalLanes } from 'react-reconciler/src/ReactFiberLane'; +import { registerDefaultIndicator } from './ReactFiberAsyncAction'; type OpaqueRoot = FiberRoot; @@ -134,7 +134,7 @@ let didWarnAboutFindNodeInStrictMode; if (__DEV__) { didWarnAboutNestedUpdates = false; - didWarnAboutFindNodeInStrictMode = ({}: {[string]: boolean}); + didWarnAboutFindNodeInStrictMode = ({}: { [string]: boolean }); } function getContextForSubtree( @@ -204,10 +204,10 @@ function findHostInstanceWithWarning( if (fiber.mode & StrictLegacyMode) { console.error( '%s is deprecated in StrictMode. ' + - '%s was passed an instance of %s which is inside StrictMode. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node', + '%s was passed an instance of %s which is inside StrictMode. ' + + 'Instead, add a ref directly to the element you want to reference. ' + + 'Learn more about using refs safely here: ' + + 'https://react.dev/link/strict-mode-find-node', methodName, methodName, componentName, @@ -215,10 +215,10 @@ function findHostInstanceWithWarning( } else { console.error( '%s is deprecated in StrictMode. ' + - '%s was passed an instance of %s which renders StrictMode children. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node', + '%s was passed an instance of %s which renders StrictMode children. ' + + 'Instead, add a ref directly to the element you want to reference. ' + + 'Learn more about using refs safely here: ' + + 'https://react.dev/link/strict-mode-find-node', methodName, methodName, componentName, @@ -242,21 +242,21 @@ export function createContainer( identifierPrefix: string, onUncaughtError: ( error: mixed, - errorInfo: {+componentStack?: ?string}, + errorInfo: {+componentStack ?: ? string}, ) => void, onCaughtError: ( error: mixed, errorInfo: { - +componentStack?: ?string, - +errorBoundary?: ?component(...props: any), + +componentStack ?: ? string, + +errorBoundary ?: ? component(...props: any), }, ) => void, onRecoverableError: ( error: mixed, - errorInfo: {+componentStack?: ?string}, + errorInfo: {+componentStack ?: ? string}, ) => void, onDefaultTransitionIndicator: () => void | (() => void), - transitionCallbacks: null | TransitionTracingCallbacks, + transitionCallbacks: null | TransitionTracingCallbacks, ): OpaqueRoot { const hydrate = false; const initialChildren = null; @@ -292,22 +292,22 @@ export function createHydrationContainer( identifierPrefix: string, onUncaughtError: ( error: mixed, - errorInfo: {+componentStack?: ?string}, + errorInfo: {+componentStack ?: ? string}, ) => void, onCaughtError: ( error: mixed, errorInfo: { - +componentStack?: ?string, - +errorBoundary?: ?component(...props: any), + +componentStack ?: ? string, + +errorBoundary ?: ? component(...props: any), }, ) => void, onRecoverableError: ( error: mixed, - errorInfo: {+componentStack?: ?string}, + errorInfo: {+componentStack ?: ? string}, ) => void, onDefaultTransitionIndicator: () => void | (() => void), - transitionCallbacks: null | TransitionTracingCallbacks, - formState: ReactFormState | null, + transitionCallbacks: null | TransitionTracingCallbacks, + formState: ReactFormState < any, any > | null, ): OpaqueRoot { const hydrate = true; const root = createFiberRoot( @@ -344,7 +344,7 @@ export function createHydrationContainer( update.callback = callback !== undefined && callback !== null ? callback : null; enqueueUpdate(current, update, lane); - startUpdateTimerByLane(lane, 'hydrateRoot()', null); + startUpdateTimerByLane(lane, 'hydrateRoot()', null, isAlreadyRendering()); scheduleInitialHydrationOnRoot(root, lane); return root; @@ -354,7 +354,7 @@ export function updateContainer( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?component(...props: any), - callback: ?Function, + callback: ?Function, ): Lane { const current = container.current; const lane = requestUpdateLane(current); @@ -373,7 +373,7 @@ export function updateContainerSync( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?component(...props: any), - callback: ?Function, + callback: ?Function, ): Lane { if (!disableLegacyMode && container.tag === LegacyRoot) { flushPendingEffects(); @@ -396,7 +396,7 @@ function updateContainerImpl( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?component(...props: any), - callback: ?Function, + callback: ?Function, ): void { if (__DEV__) { onScheduleRoot(container, element); @@ -408,53 +408,53 @@ function updateContainerImpl( const context = getContextForSubtree(parentComponent); if (container.context === null) { - container.context = context; - } else { - container.pendingContext = context; + container.context = context; +} else { + container.pendingContext = context; +} + +if (__DEV__) { + if ( + ReactCurrentFiberIsRendering && + ReactCurrentFiberCurrent !== null && + !didWarnAboutNestedUpdates + ) { + didWarnAboutNestedUpdates = true; + console.error( + 'Render methods should be a pure function of props and state; ' + + 'triggering nested component updates from render is not allowed. ' + + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + + 'Check the render method of %s.', + getComponentNameFromFiber(ReactCurrentFiberCurrent) || 'Unknown', + ); } +} + +const update = createUpdate(lane); +// Caution: React DevTools currently depends on this property +// being called "element". +update.payload = { element }; +callback = callback === undefined ? null : callback; +if (callback !== null) { if (__DEV__) { - if ( - ReactCurrentFiberIsRendering && - ReactCurrentFiberCurrent !== null && - !didWarnAboutNestedUpdates - ) { - didWarnAboutNestedUpdates = true; + if (typeof callback !== 'function') { console.error( - 'Render methods should be a pure function of props and state; ' + - 'triggering nested component updates from render is not allowed. ' + - 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + - 'Check the render method of %s.', - getComponentNameFromFiber(ReactCurrentFiberCurrent) || 'Unknown', + 'Expected the last optional `callback` argument to be a ' + + 'function. Instead received: %s.', + callback, ); } } + update.callback = callback; +} - const update = createUpdate(lane); - // Caution: React DevTools currently depends on this property - // being called "element". - update.payload = {element}; - - callback = callback === undefined ? null : callback; - if (callback !== null) { - if (__DEV__) { - if (typeof callback !== 'function') { - console.error( - 'Expected the last optional `callback` argument to be a ' + - 'function. Instead received: %s.', - callback, - ); - } - } - update.callback = callback; - } - - const root = enqueueUpdate(rootFiber, update, lane); - if (root !== null) { - startUpdateTimerByLane(lane, 'root.render()', null); - scheduleUpdateOnFiber(root, rootFiber, lane); - entangleTransitions(root, rootFiber, lane); - } +const root = enqueueUpdate(rootFiber, update, lane); +if (root !== null) { + startUpdateTimerByLane(lane, 'root.render()', null, isAlreadyRendering()); + scheduleUpdateOnFiber(root, rootFiber, lane); + entangleTransitions(root, rootFiber, lane); +} } export { @@ -562,9 +562,9 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void { markRetryLaneIfNotHydrated(fiber, lane); } -export {findHostInstance}; +export { findHostInstance }; -export {findHostInstanceWithWarning}; +export { findHostInstanceWithWarning }; export function findHostInstanceWithNoPortals( fiber: Fiber, @@ -606,7 +606,7 @@ if (__DEV__) { index: number, ): $FlowFixMe => { const key = path[index]; - const updated = isArray(obj) ? obj.slice() : {...obj}; + const updated = isArray(obj) ? obj.slice() : { ...obj }; if (index + 1 === path.length) { if (isArray(updated)) { updated.splice(((key: any): number), 1); @@ -634,7 +634,7 @@ if (__DEV__) { index: number, ): $FlowFixMe => { const oldKey = oldPath[index]; - const updated = isArray(obj) ? obj.slice() : {...obj}; + const updated = isArray(obj) ? obj.slice() : { ...obj }; if (index + 1 === oldPath.length) { const newKey = newPath[index]; // $FlowFixMe[incompatible-use] number or string is fine here @@ -688,7 +688,7 @@ if (__DEV__) { return value; } const key = path[index]; - const updated = isArray(obj) ? obj.slice() : {...obj}; + const updated = isArray(obj) ? obj.slice() : { ...obj }; // $FlowFixMe[incompatible-use] number or string is fine here updated[key] = copyWithSetImpl(obj[key], path, index + 1, value); return updated; @@ -731,7 +731,7 @@ if (__DEV__) { // (There's no appropriate action type for DevTools overrides.) // As a result though, React will see the scheduled update as a noop and bailout. // Shallow cloning props works as a workaround for now to bypass the bailout check. - fiber.memoizedProps = {...fiber.memoizedProps}; + fiber.memoizedProps = { ...fiber.memoizedProps }; const root = enqueueConcurrentRenderForLane(fiber, SyncLane); if (root !== null) { @@ -755,7 +755,7 @@ if (__DEV__) { // (There's no appropriate action type for DevTools overrides.) // As a result though, React will see the scheduled update as a noop and bailout. // Shallow cloning props works as a workaround for now to bypass the bailout check. - fiber.memoizedProps = {...fiber.memoizedProps}; + fiber.memoizedProps = { ...fiber.memoizedProps }; const root = enqueueConcurrentRenderForLane(fiber, SyncLane); if (root !== null) { @@ -780,7 +780,7 @@ if (__DEV__) { // (There's no appropriate action type for DevTools overrides.) // As a result though, React will see the scheduled update as a noop and bailout. // Shallow cloning props works as a workaround for now to bypass the bailout check. - fiber.memoizedProps = {...fiber.memoizedProps}; + fiber.memoizedProps = { ...fiber.memoizedProps }; const root = enqueueConcurrentRenderForLane(fiber, SyncLane); if (root !== null) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js index d055b271ad77..16730af7a1bc 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js @@ -7,25 +7,25 @@ * @flow */ -import {REACT_STRICT_MODE_TYPE} from 'shared/ReactSymbols'; +import { REACT_STRICT_MODE_TYPE } from 'shared/ReactSymbols'; import type { Wakeable, Thenable, GestureOptionsRequired, } from 'shared/ReactTypes'; -import type {Fiber, FiberRoot} from './ReactInternalTypes'; -import type {Lanes, Lane} from './ReactFiberLane'; -import type {ActivityState} from './ReactFiberActivityComponent'; -import type {SuspenseState} from './ReactFiberSuspenseComponent'; -import type {FunctionComponentUpdateQueue} from './ReactFiberHooks'; -import type {Transition} from 'react/src/ReactStartTransition'; +import type { Fiber, FiberRoot } from './ReactInternalTypes'; +import type { Lanes, Lane } from './ReactFiberLane'; +import type { ActivityState } from './ReactFiberActivityComponent'; +import type { SuspenseState } from './ReactFiberSuspenseComponent'; +import type { FunctionComponentUpdateQueue } from './ReactFiberHooks'; +import type { Transition } from 'react/src/ReactStartTransition'; import type { PendingTransitionCallbacks, PendingBoundaries, TransitionAbort, } from './ReactFiberTracingMarkerComponent'; -import type {OffscreenInstance} from './ReactFiberOffscreenComponent'; +import type { OffscreenInstance } from './ReactFiberOffscreenComponent'; import type { Resource, ViewTransitionInstance, @@ -33,12 +33,12 @@ import type { GestureTimeline, SuspendedState, } from './ReactFiberConfig'; -import type {RootState} from './ReactFiberRoot'; +import type { RootState } from './ReactFiberRoot'; import { getViewTransitionName, type ViewTransitionState, } from './ReactFiberViewTransitionComponent'; -import type {TransitionTypes} from 'react/src/ReactTransitionType'; +import type { TransitionTypes } from 'react/src/ReactTransitionType'; import { enableCreateEventHandleAPI, @@ -60,7 +60,7 @@ import { enableDefaultTransitionIndicator, enableParallelTransitions, } from 'shared/ReactFeatureFlags'; -import {resetOwnerStackLimit} from 'shared/ReactOwnerStackReset'; +import { resetOwnerStackLimit } from 'shared/ReactOwnerStackReset'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import is from 'shared/objectIs'; @@ -126,8 +126,8 @@ import { flushHydrationEvents, } from './ReactFiberConfig'; -import {createWorkInProgress, resetWorkInProgress} from './ReactFiber'; -import {isRootDehydrated} from './ReactFiberShellHydration'; +import { createWorkInProgress, resetWorkInProgress } from './ReactFiber'; +import { isRootDehydrated } from './ReactFiberShellHydration'; import { getIsHydrating, popHydrationStateOnInterruptedWork, @@ -154,8 +154,8 @@ import { HostHoistable, HostSingleton, } from './ReactWorkTags'; -import {ConcurrentRoot, LegacyRoot} from './ReactRootTags'; -import type {Flags} from './ReactFiberFlags'; +import { ConcurrentRoot, LegacyRoot } from './ReactRootTags'; +import type { Flags } from './ReactFiberFlags'; import { NoFlags, Incomplete, @@ -180,6 +180,7 @@ import { NoLanes, NoLane, SyncLane, + getHighestPriorityLane, claimNextRetryLane, includesSyncLane, isSubsetOfLanes, @@ -191,6 +192,7 @@ import { includesOnlyTransitions, includesBlockingLane, includesTransitionLane, + isTransitionLane, includesRetryLane, includesIdleGroupLanes, includesExpiredLane, @@ -226,14 +228,14 @@ import { lanesToEventPriority, eventPriorityToLane, } from './ReactEventPriorities'; -import {requestCurrentTransition} from './ReactFiberTransition'; +import { requestCurrentTransition } from './ReactFiberTransition'; import { SelectiveHydrationException, beginWork, replayFunctionComponent, } from './ReactFiberBeginWork'; -import {completeWork} from './ReactFiberCompleteWork'; -import {unwindWork, unwindInterruptedWork} from './ReactFiberUnwindWork'; +import { completeWork } from './ReactFiberCompleteWork'; +import { unwindWork, unwindInterruptedWork } from './ReactFiberUnwindWork'; import { throwException, createRootErrorUpdate, @@ -258,21 +260,21 @@ import { invokePassiveEffectUnmountInDEV, accumulateSuspenseyCommit, } from './ReactFiberCommitWork'; -import {resetShouldStartViewTransition} from './ReactFiberCommitViewTransitions'; -import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions'; +import { resetShouldStartViewTransition } from './ReactFiberCommitViewTransitions'; +import { shouldStartViewTransition } from './ReactFiberCommitViewTransitions'; import { insertDestinationClones, applyDepartureTransitions, startGestureAnimations, } from './ReactFiberApplyGesture'; -import {enqueueUpdate} from './ReactFiberClassUpdateQueue'; -import {resetContextDependencies} from './ReactFiberNewContext'; +import { enqueueUpdate } from './ReactFiberClassUpdateQueue'; +import { resetContextDependencies } from './ReactFiberNewContext'; import { resetHooksAfterThrow, resetHooksOnUnwind, ContextOnlyDispatcher, } from './ReactFiberHooks'; -import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher'; +import { DefaultAsyncDispatcher } from './ReactFiberAsyncDispatcher'; import { createCapturedValueAtFiber, type CapturedValue, @@ -304,21 +306,10 @@ import { gestureEventType, gestureEventRepeatTime, gestureSuspendedTime, - transitionClampTime, - transitionStartTime, - transitionUpdateTime, - transitionUpdateTask, - transitionUpdateType, - transitionUpdateMethodName, - transitionUpdateComponentName, - transitionEventTime, - transitionEventType, - transitionEventRepeatTime, - transitionSuspendedTime, clearBlockingTimers, clearGestureTimers, clearGestureUpdates, - clearTransitionTimers, + clearTransitionTimer, clampBlockingTimers, clampGestureTimers, clampTransitionTimers, @@ -350,6 +341,7 @@ import { retryClampTime, idleClampTime, animatingTask, + getTransitionTimers, } from './ReactProfilerTimer'; // DEV stuff @@ -378,13 +370,13 @@ import { onPostCommitRoot as onPostCommitRootDevTools, setIsStrictModeForDevtools, } from './ReactFiberDevToolsHook'; -import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors'; -import {releaseCache} from './ReactFiberCacheComponent'; +import { onCommitRoot as onCommitRootTestSelector } from './ReactTestSelectors'; +import { releaseCache } from './ReactFiberCacheComponent'; import { isLegacyActEnvironment, isConcurrentActEnvironment, } from './ReactFiberAct'; -import {processTransitionCallbacks} from './ReactFiberTracingMarkerComponent'; +import { processTransitionCallbacks } from './ReactFiberTracingMarkerComponent'; import { SuspenseException, SuspenseActionException, @@ -392,25 +384,25 @@ import { getSuspendedThenable, isThenableResolved, } from './ReactFiberThenable'; -import {schedulePostPaintCallback} from './ReactPostPaintCallback'; +import { schedulePostPaintCallback } from './ReactPostPaintCallback'; import { getSuspenseHandler, getShellBoundary, } from './ReactFiberSuspenseContext'; -import {resetChildReconcilerOnUnwind} from './ReactChildFiber'; +import { resetChildReconcilerOnUnwind } from './ReactChildFiber'; import { ensureRootIsScheduled, flushSyncWorkOnAllRoots, flushSyncWorkOnLegacyRootsOnly, requestTransitionLane, } from './ReactFiberRootScheduler'; -import {getMaskedContext, getUnmaskedContext} from './ReactFiberLegacyContext'; -import {logUncaughtError} from './ReactFiberErrorLogger'; +import { getMaskedContext, getUnmaskedContext } from './ReactFiberLegacyContext'; +import { logUncaughtError } from './ReactFiberErrorLogger'; import { scheduleGestureCommit, stopCommittedGesture, } from './ReactFiberGestureScheduler'; -import {claimQueuedTransitionTypes} from './ReactFiberTransitionTypes'; +import { claimQueuedTransitionTypes } from './ReactFiberTransitionTypes'; const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; @@ -559,7 +551,7 @@ export function addTransitionStartCallbackToPendingTransition( if (currentPendingTransitionCallbacks.transitionStart === null) { currentPendingTransitionCallbacks.transitionStart = - ([]: Array); + ([]: Array < Transition >); } currentPendingTransitionCallbacks.transitionStart.push(transition); @@ -693,7 +685,7 @@ export function addTransitionCompleteCallbackToPendingTransition( if (currentPendingTransitionCallbacks.transitionComplete === null) { currentPendingTransitionCallbacks.transitionComplete = - ([]: Array); + ([]: Array < Transition >); } currentPendingTransitionCallbacks.transitionComplete.push(transition); @@ -828,9 +820,9 @@ export function requestUpdateLane(fiber: Fiber): Lane { if (transition.gesture) { throw new Error( 'Cannot setState on regular state inside a startGestureTransition. ' + - 'Gestures can only update the useOptimistic() hook. There should be no ' + - 'side-effects associated with starting a Gesture until its Action is ' + - 'invoked. Move side-effects to the Action instead.', + 'Gestures can only update the useOptimistic() hook. There should be no ' + + 'side-effects associated with starting a Gesture until its Action is ' + + 'invoked. Move side-effects to the Action instead.', ); } } @@ -1036,10 +1028,6 @@ export function scheduleUpdateOnFiber( if (enableTransitionTracing) { const transition = ReactSharedInternals.T; if (transition !== null && transition.name != null) { - if (transition.startTime === -1) { - transition.startTime = now(); - } - addTransitionToLanesMap(root, transition, lane); } } @@ -1576,7 +1564,7 @@ function completeRootWhenReady( const maySuspendCommit = subtreeFlags & ShouldSuspendCommit || (subtreeFlags & BothVisibilityAndMaySuspendCommit) === - BothVisibilityAndMaySuspendCommit; + BothVisibilityAndMaySuspendCommit; let suspendedState: null | SuspendedState = null; if (isViewTransitionEligible || maySuspendCommit || isGestureTransition) { // Before committing, ask the renderer whether the host tree is ready. @@ -2141,56 +2129,66 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber { clearBlockingTimers(); } if (includesTransitionLane(lanes)) { - workInProgressUpdateTask = transitionUpdateTask; - const clampedStartTime = - transitionStartTime >= 0 && transitionStartTime < transitionClampTime - ? transitionClampTime - : transitionStartTime; - const clampedUpdateTime = - transitionUpdateTime >= 0 && transitionUpdateTime < transitionClampTime - ? transitionClampTime - : transitionUpdateTime; - const clampedEventTime = - transitionEventTime >= 0 && transitionEventTime < transitionClampTime - ? transitionClampTime - : transitionEventTime; - const clampedRenderStartTime = - // Clamp the suspended time to the first event/update. - clampedEventTime >= 0 - ? clampedEventTime - : clampedUpdateTime >= 0 - ? clampedUpdateTime - : renderStartTime; - if (transitionSuspendedTime >= 0) { - setCurrentTrackFromLanes(SomeTransitionLane); - logSuspendedWithDelayPhase( - transitionSuspendedTime, - clampedRenderStartTime, - lanes, - workInProgressUpdateTask, - ); - } else if (includesTransitionLane(animatingLanes)) { - // If this lane is still animating, log the time from previous render finishing to now as animating. - setCurrentTrackFromLanes(SomeTransitionLane); - logAnimatingPhase( - transitionClampTime, - clampedRenderStartTime, - animatingTask, - ); + let remainingLanes = lanes; + while (remainingLanes !== NoLanes) { + const lane = getHighestPriorityLane(remainingLanes); + if (isTransitionLane(lane)) { + const timers = getTransitionTimers(lane); + if (timers !== null) { + workInProgressUpdateTask = timers.updateTask; + const clampedStartTime = + timers.startTime >= 0 && timers.startTime < timers.clampTime + ? timers.clampTime + : timers.startTime; + const clampedUpdateTime = + timers.updateTime >= 0 && timers.updateTime < timers.clampTime + ? timers.clampTime + : timers.updateTime; + const clampedEventTime = + timers.eventTime >= 0 && timers.eventTime < timers.clampTime + ? timers.clampTime + : timers.eventTime; + const clampedRenderStartTime = + // Clamp the suspended time to the first event/update. + clampedEventTime >= 0 + ? clampedEventTime + : clampedUpdateTime >= 0 + ? clampedUpdateTime + : renderStartTime; + if (timers.suspendedTime >= 0) { + setCurrentTrackFromLanes(lane); + logSuspendedWithDelayPhase( + timers.suspendedTime, + clampedRenderStartTime, + lanes, + workInProgressUpdateTask, + ); + } else if (includesTransitionLane(animatingLanes)) { + // If this lane is still animating, log the time from previous render finishing to now as animating. + setCurrentTrackFromLanes(SomeTransitionLane); + logAnimatingPhase( + timers.clampTime, + clampedRenderStartTime, + animatingTask, + ); + } + logTransitionStart( + clampedStartTime, + clampedUpdateTime, + clampedEventTime, + timers.eventType, + timers.eventRepeatTime > 0, + timers.updateType === PINGED_UPDATE, + renderStartTime, + timers.updateTask, + timers.updateMethodName, + timers.updateComponentName, + ); + clearTransitionTimer(lane); + } + } + remainingLanes &= ~lane; } - logTransitionStart( - clampedStartTime, - clampedUpdateTime, - clampedEventTime, - transitionEventType, - transitionEventRepeatTime > 0, - transitionUpdateType === PINGED_UPDATE, - renderStartTime, - transitionUpdateTask, - transitionUpdateMethodName, - transitionUpdateComponentName, - ); - clearTransitionTimers(); } if (includesRetryLane(lanes)) { if (includesRetryLane(animatingLanes)) { @@ -2333,11 +2331,11 @@ function handleThrow(root: FiberRoot, thrownValue: any): void { workInProgressSuspendedReason = isWakeable ? // A wakeable object was thrown by a legacy Suspense implementation. - // This has slightly different behavior than suspending with `use`. - SuspendedOnDeprecatedThrowPromise + // This has slightly different behavior than suspending with `use`. + SuspendedOnDeprecatedThrowPromise : // This is a regular error. If something earlier in the component already - // suspended, we must clear the thenable state to unblock the work loop. - SuspendedOnError; + // suspended, we must clear the thenable state to unblock the work loop. + SuspendedOnError; } workInProgressThrownValue = thrownValue; @@ -2929,7 +2927,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes): RootExitStatus { if (__DEV__) { console.error( 'Unexpected type of fiber triggered a suspensey commit. ' + - 'This is a bug in React.', + 'This is a bug in React.', ); } break; @@ -3540,7 +3538,7 @@ function completeRoot( finishedWork !== null && finishedWork.alternate !== null && (finishedWork.alternate.memoizedState: RootState).isDehydrated && - (finishedWork.flags & ForceClientRender) !== NoFlags; + (finishedWork.flags & ForceClientRender) !== NoFlags; logRecoveredRenderPhase( completedRenderStartTime, completedRenderEndTime, @@ -3579,7 +3577,7 @@ function completeRoot( if (lanes === NoLanes) { console.error( 'finishedLanes should not be empty during a commit. This is a ' + - 'bug in React.', + 'bug in React.', ); } } @@ -3588,7 +3586,7 @@ function completeRoot( if (finishedWork === root.current) { throw new Error( 'Cannot commit the same tree as before. This error is likely caused by ' + - 'a bug in React. Please file an issue.', + 'a bug in React. Please file an issue.', ); } @@ -3882,7 +3880,7 @@ function commitRoot( enableProfilerTimer ? suspendedViewTransition : (null: any), enableProfilerTimer ? // This callback fires after "pendingEffects" so we need to snapshot the arguments. - finishedViewTransition.bind(null, lanes) + finishedViewTransition.bind(null, lanes) : (null: any), ); } else { @@ -3945,8 +3943,18 @@ function finishedViewTransition(lanes: Lanes): void { !includesTransitionLane(workInProgressRootRenderLanes) && !includesTransitionLane(pendingEffectsLanes) ) { - setCurrentTrackFromLanes(SomeTransitionLane); - logAnimatingPhase(transitionClampTime, now(), task); + let remainingLanes = lanes; + while (remainingLanes !== NoLanes) { + const lane = getHighestPriorityLane(remainingLanes); + if (isTransitionLane(lane)) { + const timers = getTransitionTimers(lane); + if (timers !== null) { + setCurrentTrackFromLanes(lane); + logAnimatingPhase(timers.clampTime, now(), task); + } + } + remainingLanes = removeLanes(remainingLanes, lane); + } } if ( includesRetryLane(lanes) && @@ -4441,7 +4449,7 @@ function applyGestureOnRoot( reportViewTransitionError, enableProfilerTimer ? // This callback fires after "pendingEffects" so we need to snapshot the arguments. - finishedViewTransition.bind(null, pendingEffectsLanes) + finishedViewTransition.bind(null, pendingEffectsLanes) : (null: any), ); } @@ -4582,8 +4590,8 @@ function makeErrorInfo(componentStack: ?string) { get() { console.error( 'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' + - ' This property is no longer provided as part of errorInfo but can be accessed as a property' + - ' of the Error instance itself.', + ' This property is no longer provided as part of errorInfo but can be accessed as a property' + + ' of the Error instance itself.', ); }, }); @@ -4624,9 +4632,9 @@ export function flushPendingEffects(): boolean { didWarnAboutInterruptedViewTransitions = true; console.warn( 'A flushSync update cancelled a View Transition because it was called ' + - 'while the View Transition was still preparing. To preserve the synchronous ' + - 'semantics, React had to skip the View Transition. If you can, try to avoid ' + - "flushSync() in a scenario that's likely to interfere.", + 'while the View Transition was still preparing. To preserve the synchronous ' + + 'semantics, React had to skip the View Transition. If you can, try to avoid ' + + "flushSync() in a scenario that's likely to interfere.", ); } } @@ -4912,10 +4920,10 @@ export function captureCommitPhaseError( if (__DEV__) { console.error( 'Internal React error: Attempted to capture a commit phase error ' + - 'inside a detached tree. This indicates a bug in React. Potential ' + - 'causes include deleting the same fiber more than once, committing an ' + - 'already-finished tree, or an inconsistent return pointer.\n\n' + - 'Error message:\n\n%s', + 'inside a detached tree. This indicates a bug in React. Potential ' + + 'causes include deleting the same fiber more than once, committing an ' + + 'already-finished tree, or an inconsistent return pointer.\n\n' + + 'Error message:\n\n%s', error, ); } @@ -4942,7 +4950,7 @@ export function attachPingListener( let threadIDs; if (pingCache === null) { pingCache = root.pingCache = new PossiblyWeakMap(); - threadIDs = new Set(); + threadIDs = new Set < mixed > (); pingCache.set(wakeable, threadIDs); } else { threadIDs = pingCache.get(wakeable); @@ -5132,7 +5140,7 @@ export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) { default: throw new Error( 'Pinged unknown suspense boundary type. ' + - 'This is probably a bug in React.', + 'This is probably a bug in React.', ); } @@ -5167,9 +5175,9 @@ export function throwIfInfiniteUpdateLoopDetected() { throw new Error( 'Maximum update depth exceeded. This can happen when a component ' + - 'repeatedly calls setState inside componentWillUpdate or ' + - 'componentDidUpdate. React limits the number of nested updates to ' + - 'prevent infinite loops.', + 'repeatedly calls setState inside componentWillUpdate or ' + + 'componentDidUpdate. React limits the number of nested updates to ' + + 'prevent infinite loops.', ); } @@ -5180,9 +5188,9 @@ export function throwIfInfiniteUpdateLoopDetected() { console.error( 'Maximum update depth exceeded. This can happen when a component ' + - "calls setState inside useEffect, but useEffect either doesn't " + - 'have a dependency array, or one of the dependencies changes on ' + - 'every render.', + "calls setState inside useEffect, but useEffect either doesn't " + + 'have a dependency array, or one of the dependencies changes on ' + + 'every render.', ); } } @@ -5393,9 +5401,9 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) { runWithFiberInDEV(fiber, () => { console.error( "Can't perform a React state update on a component that hasn't mounted yet. " + - 'This indicates that you have a side-effect in your render function that ' + - 'asynchronously tries to update the component. Move this work to ' + - 'useEffect instead.', + 'This indicates that you have a side-effect in your render function that ' + + 'asynchronously tries to update the component. Move this work to ' + + 'useEffect instead.', ); }); } @@ -5404,7 +5412,7 @@ export function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber: Fiber) { let didWarnAboutUpdateInRender = false; let didWarnAboutUpdateInRenderForAnotherComponent; if (__DEV__) { - didWarnAboutUpdateInRenderForAnotherComponent = new Set(); + didWarnAboutUpdateInRenderForAnotherComponent = new Set < string > (); } function warnAboutRenderPhaseUpdatesInDEV(fiber: Fiber) { @@ -5425,8 +5433,8 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber: Fiber) { getComponentNameFromFiber(fiber) || 'Unknown'; console.error( 'Cannot update a component (`%s`) while rendering a ' + - 'different component (`%s`). To locate the bad setState() call inside `%s`, ' + - 'follow the stack trace as described in https://react.dev/link/setstate-in-render', + 'different component (`%s`). To locate the bad setState() call inside `%s`, ' + + 'follow the stack trace as described in https://react.dev/link/setstate-in-render', setStateComponentName, renderingComponentName, renderingComponentName, @@ -5438,8 +5446,8 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber: Fiber) { if (!didWarnAboutUpdateInRender) { console.error( 'Cannot update during an existing state transition (such as ' + - 'within `render`). Render methods should be a pure ' + - 'function of props and state.', + 'within `render`). Render methods should be a pure ' + + 'function of props and state.', ); didWarnAboutUpdateInRender = true; } @@ -5522,15 +5530,15 @@ function warnIfUpdatesNotWrappedWithActDEV(fiber: Fiber): void { runWithFiberInDEV(fiber, () => { console.error( 'An update to %s inside a test was not wrapped in act(...).\n\n' + - 'When testing, code that causes React state updates should be ' + - 'wrapped into act(...):\n\n' + - 'act(() => {\n' + - ' /* fire events that update state */\n' + - '});\n' + - '/* assert on the output */\n\n' + - "This ensures that you're testing the behavior the user would see " + - 'in the browser.' + - ' Learn more at https://react.dev/link/wrap-tests-with-act', + 'When testing, code that causes React state updates should be ' + + 'wrapped into act(...):\n\n' + + 'act(() => {\n' + + ' /* fire events that update state */\n' + + '});\n' + + '/* assert on the output */\n\n' + + "This ensures that you're testing the behavior the user would see " + + 'in the browser.' + + ' Learn more at https://react.dev/link/wrap-tests-with-act', getComponentNameFromFiber(fiber), ); }); @@ -5547,16 +5555,16 @@ function warnIfSuspenseResolutionNotWrappedWithActDEV(root: FiberRoot): void { ) { console.error( 'A suspended resource finished loading inside a test, but the event ' + - 'was not wrapped in act(...).\n\n' + - 'When testing, code that resolves suspended data should be wrapped ' + - 'into act(...):\n\n' + - 'act(() => {\n' + - ' /* finish loading suspended data */\n' + - '});\n' + - '/* assert on the output */\n\n' + - "This ensures that you're testing the behavior the user would see " + - 'in the browser.' + - ' Learn more at https://react.dev/link/wrap-tests-with-act', + 'was not wrapped in act(...).\n\n' + + 'When testing, code that resolves suspended data should be wrapped ' + + 'into act(...):\n\n' + + 'act(() => {\n' + + ' /* finish loading suspended data */\n' + + '});\n' + + '/* assert on the output */\n\n' + + "This ensures that you're testing the behavior the user would see " + + 'in the browser.' + + ' Learn more at https://react.dev/link/wrap-tests-with-act', ); } } diff --git a/packages/react-reconciler/src/ReactProfilerTimer.js b/packages/react-reconciler/src/ReactProfilerTimer.js index 42289ea30a1d..5f8004baaef1 100644 --- a/packages/react-reconciler/src/ReactProfilerTimer.js +++ b/packages/react-reconciler/src/ReactProfilerTimer.js @@ -7,13 +7,14 @@ * @flow */ -import type {Fiber} from './ReactInternalTypes'; +import type { Fiber } from './ReactInternalTypes'; -import type {SuspendedReason} from './ReactFiberWorkLoop'; +import type { SuspendedReason } from './ReactFiberWorkLoop'; +import type { Transition } from 'react/src/ReactStartTransition'; -import type {Lane, Lanes} from './ReactFiberLane'; +import type { Lane, Lanes } from './ReactFiberLane'; -import type {CapturedValue} from './ReactCapturedValue'; +import type { CapturedValue } from './ReactCapturedValue'; import { isTransitionLane, @@ -21,10 +22,11 @@ import { isGestureRender, includesTransitionLane, includesBlockingLane, + getHighestPriorityLane, NoLanes, } from './ReactFiberLane'; -import {resolveEventType, resolveEventTimeStamp} from './ReactFiberConfig'; +import { resolveEventType, resolveEventTimeStamp } from './ReactFiberConfig'; import { enableProfilerCommitHooks, @@ -34,19 +36,19 @@ import { } from 'shared/ReactFeatureFlags'; import getComponentNameFromFiber from './getComponentNameFromFiber'; -import {isAlreadyRendering} from './ReactFiberWorkLoop'; +import { requestCurrentTransition } from './ReactFiberTransition'; // Intentionally not named imports because Rollup would use dynamic dispatch for // CommonJS interop named imports. import * as Scheduler from 'scheduler'; -const {unstable_now: now} = Scheduler; +const { unstable_now: now } = Scheduler; const createTask = // eslint-disable-next-line react-internal/no-production-logging __DEV__ && console.createTask ? // eslint-disable-next-line react-internal/no-production-logging - console.createTask + console.createTask : (name: string) => null; export const REGULAR_UPDATE: UpdateType = 0; @@ -89,17 +91,35 @@ export let gestureEventRepeatTime: number = -1.1; export let gestureSuspendedTime: number = -1.1; // TODO: This should really be one per Transition lane. -export let transitionClampTime: number = -0; -export let transitionStartTime: number = -1.1; // First startTransition call before setState. -export let transitionUpdateTime: number = -1.1; // First transition setState scheduled. -export let transitionUpdateType: UpdateType = 0; -export let transitionUpdateTask: null | ConsoleTask = null; // First transition setState's stack trace. -export let transitionUpdateMethodName: null | string = null; // The name of the method that caused first transition update. -export let transitionUpdateComponentName: null | string = null; // The name of the component where first transition update happened. -export let transitionEventTime: number = -1.1; // Event timeStamp of the first transition. -export let transitionEventType: null | string = null; // Event type of the first transition. -export let transitionEventRepeatTime: number = -1.1; -export let transitionSuspendedTime: number = -1.1; +export const transitionTimers: Map = new Map(); +const pendingTransitionStartTimes: WeakMap = new WeakMap(); +const pendingTransitionEventInfo: WeakMap< + Transition, + { + time: number, + type: null | string, + }, +> = new WeakMap(); +const pendingActionUpdateTimes: WeakMap = new WeakMap(); + +export function getTransitionTimers(lane: Lane): TransitionTimers | null { + const timers = transitionTimers.get(lane); + return timers !== undefined ? timers : null; +} + +export type TransitionTimers = { + clampTime: number, + startTime: number, + updateTime: number, + updateType: UpdateType, + updateTask: null | ConsoleTask, + updateMethodName: null | string, + updateComponentName: null | string, + eventTime: number, + eventType: null | string, + eventRepeatTime: number, + suspendedTime: number, +}; export let retryClampTime: number = -0; export let idleClampTime: number = -0; @@ -122,6 +142,7 @@ export function startUpdateTimerByLane( lane: Lane, method: string, fiber: Fiber | null, + isAlreadyRendering: boolean, ): void { if (!enableProfilerTimer || !enableComponentPerformanceTrack) { return; @@ -153,7 +174,7 @@ export function startUpdateTimerByLane( if (__DEV__ && fiber != null) { blockingUpdateComponentName = getComponentNameFromFiber(fiber); } - if (isAlreadyRendering()) { + if (isAlreadyRendering) { componentEffectSpawnedUpdate = true; blockingUpdateType = SPAWNED_UPDATE; } @@ -174,30 +195,74 @@ export function startUpdateTimerByLane( blockingEventType = newEventType; } } else if (isTransitionLane(lane)) { - if (transitionUpdateTime < 0) { - transitionUpdateTime = now(); - transitionUpdateTask = createTask(method); - transitionUpdateMethodName = method; + let timers = transitionTimers.get(lane); + if (timers === undefined) { + timers = { + clampTime: -0, + startTime: -1.1, + updateTime: -1.1, + updateType: 0, + updateTask: null, + updateMethodName: null, + updateComponentName: null, + eventTime: -1.1, + eventType: null, + eventRepeatTime: -1.1, + suspendedTime: -1.1, + }; + transitionTimers.set(lane, timers); + } + if (timers.updateTime < 0) { + const pendingUpdateTime = + fiber !== null ? pendingActionUpdateTimes.get(fiber) : undefined; + timers.updateTime = + pendingUpdateTime !== undefined ? pendingUpdateTime : now(); + timers.updateTask = createTask(method); + timers.updateMethodName = method; if (__DEV__ && fiber != null) { - transitionUpdateComponentName = getComponentNameFromFiber(fiber); + timers.updateComponentName = getComponentNameFromFiber(fiber); } - if (transitionStartTime < 0) { - const newEventTime = resolveEventTimeStamp(); - const newEventType = resolveEventType(); + if (timers.startTime < 0) { + const transition = requestCurrentTransition(); + const pendingStartTime = + transition !== null + ? pendingTransitionStartTimes.get(transition) + : undefined; + if (pendingStartTime !== undefined) { + timers.startTime = pendingStartTime; + } + + let newEventTime = -1.1; + let newEventType = null; + const pendingEventInfo = + transition !== null + ? pendingTransitionEventInfo.get(transition) + : undefined; + if (pendingEventInfo !== undefined) { + newEventTime = pendingEventInfo.time; + newEventType = pendingEventInfo.type; + } else { + newEventTime = resolveEventTimeStamp(); + newEventType = resolveEventType(); + } + if ( - newEventTime !== transitionEventRepeatTime || - newEventType !== transitionEventType + newEventTime !== timers.eventRepeatTime || + newEventType !== timers.eventType ) { - transitionEventRepeatTime = -1.1; + timers.eventRepeatTime = -1.1; } - transitionEventTime = newEventTime; - transitionEventType = newEventType; + timers.eventTime = newEventTime; + timers.eventType = newEventType; } } } } -export function startHostActionTimer(fiber: Fiber): void { +export function startHostActionTimer( + fiber: Fiber, + isAlreadyRendering: boolean, +): void { if (!enableProfilerTimer || !enableComponentPerformanceTrack) { return; } @@ -207,7 +272,7 @@ export function startHostActionTimer(fiber: Fiber): void { blockingUpdateTime = now(); blockingUpdateTask = __DEV__ && fiber._debugTask != null ? fiber._debugTask : null; - if (isAlreadyRendering()) { + if (isAlreadyRendering) { blockingUpdateType = SPAWNED_UPDATE; } const newEventTime = resolveEventTimeStamp(); @@ -226,21 +291,18 @@ export function startHostActionTimer(fiber: Fiber): void { blockingEventTime = newEventTime; blockingEventType = newEventType; } - if (transitionUpdateTime < 0) { - transitionUpdateTime = now(); - transitionUpdateTask = - __DEV__ && fiber._debugTask != null ? fiber._debugTask : null; - if (transitionStartTime < 0) { + pendingActionUpdateTimes.set(fiber, now()); + + const transition = requestCurrentTransition(); + if (transition !== null) { + if (!pendingTransitionStartTimes.has(transition)) { + pendingTransitionStartTimes.set(transition, now()); const newEventTime = resolveEventTimeStamp(); const newEventType = resolveEventType(); - if ( - newEventTime !== transitionEventRepeatTime || - newEventType !== transitionEventType - ) { - transitionEventRepeatTime = -1.1; - } - transitionEventTime = newEventTime; - transitionEventType = newEventType; + pendingTransitionEventInfo.set(transition, { + time: newEventTime, + type: newEventType, + }); } } } @@ -265,10 +327,34 @@ export function startPingTimerByLanes(lanes: Lanes): void { blockingUpdateType = PINGED_UPDATE; } } else if (includesTransitionLane(lanes)) { - if (transitionUpdateTime < 0) { - transitionClampTime = transitionUpdateTime = now(); - transitionUpdateTask = createTask('Promise Resolved'); - transitionUpdateType = PINGED_UPDATE; + let remainingLanes = lanes; + while (remainingLanes !== NoLanes) { + const lane = getHighestPriorityLane(remainingLanes); + if (isTransitionLane(lane)) { + let timers = transitionTimers.get(lane); + if (timers === undefined) { + timers = { + clampTime: -0, + startTime: -1.1, + updateTime: -1.1, + updateType: 0, + updateTask: null, + updateMethodName: null, + updateComponentName: null, + eventTime: -1.1, + eventType: null, + eventRepeatTime: -1.1, + suspendedTime: -1.1, + }; + transitionTimers.set(lane, timers); + } + if (timers.updateTime < 0) { + timers.clampTime = timers.updateTime = now(); + timers.updateTask = createTask('Promise Resolved'); + timers.updateType = PINGED_UPDATE; + } + } + remainingLanes &= ~lane; } } } @@ -282,7 +368,17 @@ export function trackSuspendedTime(lanes: Lanes, renderEndTime: number) { } else if (includesBlockingLane(lanes)) { blockingSuspendedTime = renderEndTime; } else if (includesTransitionLane(lanes)) { - transitionSuspendedTime = renderEndTime; + let remainingLanes = lanes; + while (remainingLanes !== NoLanes) { + const lane = getHighestPriorityLane(remainingLanes); + if (isTransitionLane(lane)) { + const timers = transitionTimers.get(lane); + if (timers !== undefined) { + timers.suspendedTime = renderEndTime; + } + } + remainingLanes &= ~lane; + } } } @@ -301,38 +397,36 @@ export function startAsyncTransitionTimer(): void { if (!enableProfilerTimer || !enableComponentPerformanceTrack) { return; } - if (transitionStartTime < 0 && transitionUpdateTime < 0) { - transitionStartTime = now(); - const newEventTime = resolveEventTimeStamp(); - const newEventType = resolveEventType(); - if ( - newEventTime !== transitionEventRepeatTime || - newEventType !== transitionEventType - ) { - transitionEventRepeatTime = -1.1; + const transition = requestCurrentTransition(); + if (transition !== null) { + if (!pendingTransitionStartTimes.has(transition)) { + pendingTransitionStartTimes.set(transition, now()); + const newEventTime = resolveEventTimeStamp(); + const newEventType = resolveEventType(); + pendingTransitionEventInfo.set(transition, { + time: newEventTime, + type: newEventType, + }); } - transitionEventTime = newEventTime; - transitionEventType = newEventType; } } export function hasScheduledTransitionWork(): boolean { // If we have setState on a transition or scheduled useActionState update. - return transitionUpdateTime > -1; + // If we have setState on a transition or scheduled useActionState update. + return Array.from(transitionTimers.values()).some((timers) => { + return timers.updateTime > -1; + }); } export function clearAsyncTransitionTimer(): void { - transitionStartTime = -1.1; + // This is a global clear. We probably shouldn't use it or it should be + // updated to clear pending weak maps? + // For now, removing the global write. } -export function clearTransitionTimers(): void { - transitionStartTime = -1.1; - transitionUpdateTime = -1.1; - transitionUpdateType = 0; - transitionSuspendedTime = -1.1; - transitionEventRepeatTime = transitionEventTime; - transitionEventTime = -1.1; - transitionClampTime = now(); +export function clearTransitionTimer(lane: Lane): void { + transitionTimers.delete(lane); } export function hasScheduledGestureTransitionWork(): boolean { @@ -386,7 +480,9 @@ export function clampTransitionTimers(finalTime: number): void { // If we had new updates come in while we were still rendering or committing, we don't want // those update times to create overlapping tracks in the performance timeline so we clamp // them to the end of the commit phase. - transitionClampTime = finalTime; + transitionTimers.forEach((timers) => { + timers.clampTime = finalTime; + }); } export function clampRetryTimers(finalTime: number): void { diff --git a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js index 42d3ee57a613..67643c8baf76 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js @@ -145,13 +145,13 @@ describe('ReactInteractionTracing', () => { } } - function AsyncText({text}) { + function AsyncText({ text }) { const fullText = readText(text); Scheduler.log(fullText); return fullText; } - function Text({text}) { + function Text({ text }) { Scheduler.log(text); return text; } @@ -177,7 +177,7 @@ describe('ReactInteractionTracing', () => { jest.advanceTimersByTime(ms); // Wait until the end of the current tick // We cannot use a timer since we're faking them - return Promise.resolve().then(() => {}); + return Promise.resolve().then(() => { }); } // @gate enableTransitionTracing @@ -216,7 +216,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({navigate}) { + function App({ navigate }) { return (
{navigate ? ( @@ -305,7 +305,7 @@ describe('ReactInteractionTracing', () => { await waitForAll(['Page One']); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -367,7 +367,7 @@ describe('ReactInteractionTracing', () => { navigateToPageTwo(); setText(); }, - {name: 'page transition'}, + { name: 'page transition' }, ); ReactNoop.expire(1000); @@ -434,7 +434,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -530,7 +530,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); await waitForAll([ 'Suspend [Page Two]', @@ -551,7 +551,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionComplete(page transition, 1000, 2000)', ]); - startTransition(() => showTextFn(), {name: 'text transition'}); + startTransition(() => showTextFn(), { name: 'text transition' }); await waitForAll([ 'Suspend [Show Text]', 'Show Text Loading...', @@ -641,7 +641,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -657,7 +657,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => showTextFn(), {name: 'show text'}); + startTransition(() => showTextFn(), { name: 'show text' }); await waitForAll([ 'Suspend [Show Text]', @@ -763,7 +763,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -899,8 +899,8 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => setNavigate(), {name: 'navigate'}); - startTransition(() => setShowTextOne(), {name: 'show text one'}); + startTransition(() => setNavigate(), { name: 'navigate' }); + startTransition(() => setShowTextOne(), { name: 'show text one' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -937,7 +937,7 @@ describe('ReactInteractionTracing', () => { 'onTransitionProgress(show text one, 1000, 3000, [show text one, ])', ]); - startTransition(() => setShowTextTwo(), {name: 'show text two'}); + startTransition(() => setShowTextTwo(), { name: 'show text two' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -1064,7 +1064,7 @@ describe('ReactInteractionTracing', () => { await waitForAll(['Page One']); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -1150,7 +1150,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -1272,7 +1272,7 @@ describe('ReactInteractionTracing', () => { }); await act(async () => { - startTransition(() => navigateToPageTwo(), {name: 'page transition'}); + startTransition(() => navigateToPageTwo(), { name: 'page transition' }); ReactNoop.expire(1000); await advanceTimers(1000); @@ -1373,7 +1373,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({navigate, markerName}) { + function App({ navigate, markerName }) { return (
{navigate ? ( @@ -1485,7 +1485,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({navigate, showMarker}) { + function App({ navigate, showMarker }) { return (
{navigate ? ( @@ -1650,7 +1650,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({navigate, deleteOne}) { + function App({ navigate, deleteOne }) { return (
{navigate ? ( @@ -1790,7 +1790,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({navigate, deleteOne}) { + function App({ navigate, deleteOne }) { return (
{navigate ? ( @@ -1957,7 +1957,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({show}) { + function App({ show }) { return ( {show ? ( @@ -2078,7 +2078,7 @@ describe('ReactInteractionTracing', () => { }, }; - function App({show, showSuspense}) { + function App({ show, showSuspense }) { return ( {show ? ( @@ -2206,7 +2206,7 @@ describe('ReactInteractionTracing', () => { ); }, }; - function App({markerName, markerKey}) { + function App({ markerName, markerKey }) { return ( @@ -2249,8 +2249,8 @@ describe('ReactInteractionTracing', () => { ]); assertConsoleErrorDev([ 'Changing the name of a tracing marker after mount is not supported. ' + - 'To remount the tracing marker, pass it a new key.\n' + - ' in App (at **)', + 'To remount the tracing marker, pass it a new key.\n' + + ' in App (at **)', ]); startTransition( () => root.render(), @@ -2307,7 +2307,7 @@ describe('ReactInteractionTracing', () => { unstable_transitionCallbacks: transitionCallbacks, }); await act(() => { - startTransition(() => root.render(), {name: 'transition'}); + startTransition(() => root.render(), { name: 'transition' }); ReactNoop.expire(1000); advanceTimers(1000); }); @@ -2373,7 +2373,7 @@ describe('ReactInteractionTracing', () => { await act(async () => { ReactNoop.discreteUpdates(() => - startTransition(() => root.render(), {name: 'page transition'}), + startTransition(() => root.render(), { name: 'page transition' }), ); ReactNoop.expire(1000); await advanceTimers(1000); @@ -2443,7 +2443,7 @@ describe('ReactInteractionTracing', () => { }); await act(() => { - startTransition(() => root.render(), {name: 'transition'}); + startTransition(() => root.render(), { name: 'transition' }); ReactNoop.expire(1000); advanceTimers(1000); }); @@ -2500,7 +2500,7 @@ describe('ReactInteractionTracing', () => { }; }; - function App({name}) { + function App({ name }) { return ( <> }> @@ -2568,4 +2568,107 @@ describe('ReactInteractionTracing', () => { 'onTransitionComplete(transition two, 0, 3000) /root two/', ]); }); + // @gate enableTransitionTracing + it('should correctly track start times for interleaved transitions (regression test)', async () => { + const transitionCallbacks = { + onTransitionStart: (name, startTime) => { + Scheduler.log(`onTransitionStart(${name}, ${startTime})`); + }, + onTransitionComplete: (name, startTime, endTime) => { + Scheduler.log( + `onTransitionComplete(${name}, ${startTime}, ${endTime})`, + ); + }, + }; + + function App({ name }) { + return ( + }> + + + ); + } + + // Use two separate roots to simulate independent actions that might otherwise race on global state + const rootOne = ReactNoop.createRoot({ + unstable_transitionCallbacks: transitionCallbacks, + }); + + const rootTwo = ReactNoop.createRoot({ + unstable_transitionCallbacks: transitionCallbacks, + }); + + // 1. Initial State + await act(async () => { + rootOne.render(null); + rootTwo.render(null); + ReactNoop.expire(1000); + await advanceTimers(1000); + }); + + // 2. Start Transition A at 1000ms + await act(async () => { + startTransition(() => rootOne.render(), { + name: 'Transition A', + }); + + await waitForAll([ + 'Suspend [Text A]', + 'Loading A...', + 'Suspend [Text A]', // Pre-warm + 'onTransitionStart(Transition A, 1000)', + ]); + }); + + // 3. Advance to 2000ms + await act(async () => { + ReactNoop.expire(1000); + await advanceTimers(1000); + }); + + // 4. Start Transition B at 2000ms + await act(async () => { + startTransition(() => rootTwo.render(), { + name: 'Transition B', + }); + + await waitForAll([ + 'Suspend [Text B]', + 'Loading B...', + 'Suspend [Text B]', // Pre-warm + 'onTransitionStart(Transition B, 2000)', + ]); + }); + + // 5. Advance to 3000ms + await act(async () => { + ReactNoop.expire(1000); + await advanceTimers(1000); + }); + + // 6. Resolve A. It started at 1000ms. + // If globals were clobbered by B, it might say 2000ms or some other wrong time. + await act(async () => { + await resolveText('Text A'); + ReactNoop.expire(1000); + await advanceTimers(1000); + }); + + assertLog([ + 'Text A', + 'onTransitionComplete(Transition A, 1000, 4000)', + ]); + + // 7. Resolve B. Started at 2000ms. + await act(async () => { + await resolveText('Text B'); + ReactNoop.expire(1000); + await advanceTimers(1000); + }); + + assertLog([ + 'Text B', + 'onTransitionComplete(Transition B, 2000, 5000)', + ]); + }); }); diff --git a/packages/react/src/ReactStartTransition.js b/packages/react/src/ReactStartTransition.js index 3e353a3b6153..987dc51a6294 100644 --- a/packages/react/src/ReactStartTransition.js +++ b/packages/react/src/ReactStartTransition.js @@ -42,6 +42,12 @@ function releaseAsyncTransition() { } } + +const now = + typeof performance === 'object' && typeof performance.now === 'function' + ? () => performance.now() + : () => Date.now(); + export function startTransition( scope: () => void, options?: StartTransitionOptions, @@ -65,7 +71,7 @@ export function startTransition( if (enableTransitionTracing) { currentTransition.name = options !== undefined && options.name !== undefined ? options.name : null; - currentTransition.startTime = -1; // TODO: This should read the timestamp. + currentTransition.startTime = now(); } if (__DEV__) { currentTransition._updatedFibers = new Set(); @@ -146,7 +152,7 @@ export function startGestureTransition( if (enableTransitionTracing) { currentTransition.name = options !== undefined && options.name !== undefined ? options.name : null; - currentTransition.startTime = -1; // TODO: This should read the timestamp. + currentTransition.startTime = now(); } if (__DEV__) { currentTransition._updatedFibers = new Set();