From 88ee14ffa57beb0689f26f0c52c357e3ac446af8 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 1 Aug 2024 10:55:53 +0200 Subject: [PATCH] [Devtools] Ensure initial read of `useFormStatus` returns `NotPendingTransition` (#28728) --- packages/react-art/src/ReactFiberConfigART.js | 12 +++++++ ...ReactHooksInspectionIntegrationDOM-test.js | 7 ++++- .../src/client/ReactFiberConfigDOM.js | 11 ++++++- .../src/ReactFiberConfigFabric.js | 10 ++++++ .../src/ReactFiberConfigNative.js | 11 +++++++ .../src/ReactFiberBeginWork.js | 2 +- .../react-reconciler/src/ReactFiberHooks.js | 5 ++- .../src/ReactFiberHostContext.js | 31 ++++--------------- .../src/ReactFiberNewContext.js | 7 ++--- .../src/forks/ReactFiberConfig.custom.js | 1 + .../src/ReactFiberConfigTestHost.js | 11 +++++++ 11 files changed, 72 insertions(+), 36 deletions(-) diff --git a/packages/react-art/src/ReactFiberConfigART.js b/packages/react-art/src/ReactFiberConfigART.js index e80e6100ec534..816353e0ced2f 100644 --- a/packages/react-art/src/ReactFiberConfigART.js +++ b/packages/react-art/src/ReactFiberConfigART.js @@ -16,6 +16,8 @@ import { DefaultEventPriority, NoEventPriority, } from 'react-reconciler/src/ReactEventPriorities'; +import type {ReactContext} from 'shared/ReactTypes'; +import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; export {default as rendererVersion} from 'shared/ReactVersion'; export const rendererPackageName = 'react-art'; @@ -28,6 +30,8 @@ if (__DEV__) { Object.freeze(NO_CONTEXT); } +export type TransitionStatus = mixed; + /** Helper Methods */ function addEventListeners(instance, type, listener) { @@ -488,4 +492,12 @@ export function waitForCommitToBeReady() { } export const NotPendingTransition = null; +export const HostTransitionContext: ReactContext = { + $$typeof: REACT_CONTEXT_TYPE, + Provider: (null: any), + Consumer: (null: any), + _currentValue: NotPendingTransition, + _currentValue2: NotPendingTransition, + _threadCount: 0, +}; export function resetFormInstance() {} diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegrationDOM-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegrationDOM-test.js index 752d93fa30ab8..3ef3c2f3a7b7b 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegrationDOM-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegrationDOM-test.js @@ -119,7 +119,12 @@ describe('ReactHooksInspectionIntegration', () => { isStateEditable: false, name: 'FormStatus', subHooks: [], - value: null, + value: { + action: null, + data: null, + method: null, + pending: false, + }, }, { debugInfo: null, diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 28d4cf73aab7b..d70d1fdbb381c 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -14,7 +14,7 @@ import type { IntersectionObserverOptions, ObserveVisibleRectsCallback, } from 'react-reconciler/src/ReactTestSelectors'; -import type {ReactScopeInstance} from 'shared/ReactTypes'; +import type {ReactContext, ReactScopeInstance} from 'shared/ReactTypes'; import type {AncestorInfoDev} from './validateDOMNesting'; import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions'; import type { @@ -32,6 +32,7 @@ import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostCo import hasOwnProperty from 'shared/hasOwnProperty'; import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion'; +import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; export { setCurrentUpdatePriority, @@ -3564,6 +3565,14 @@ function insertStylesheetIntoRoot( } export const NotPendingTransition: TransitionStatus = NotPending; +export const HostTransitionContext: ReactContext = { + $$typeof: REACT_CONTEXT_TYPE, + Provider: (null: any), + Consumer: (null: any), + _currentValue: NotPendingTransition, + _currentValue2: NotPendingTransition, + _threadCount: 0, +}; export type FormInstance = HTMLFormElement; export function resetFormInstance(form: FormInstance): void { diff --git a/packages/react-native-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 8e04289fb1c0b..80215d11ec14c 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -60,6 +60,8 @@ import { enableFabricCompleteRootInCommitPhase, passChildrenWhenCloningPersistedNodes, } from 'shared/ReactFeatureFlags'; +import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import type {ReactContext} from 'shared/ReactTypes'; export {default as rendererVersion} from 'shared/ReactVersion'; // TODO: Consider exporting the react-native version. export const rendererPackageName = 'react-native-renderer'; @@ -544,6 +546,14 @@ export function waitForCommitToBeReady(): null { } export const NotPendingTransition: TransitionStatus = null; +export const HostTransitionContext: ReactContext = { + $$typeof: REACT_CONTEXT_TYPE, + Provider: (null: any), + Consumer: (null: any), + _currentValue: NotPendingTransition, + _currentValue2: NotPendingTransition, + _threadCount: 0, +}; export type FormInstance = Instance; export function resetFormInstance(form: Instance): void {} diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js index c9c6aadcf919c..0a95e0f818cdb 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ b/packages/react-native-renderer/src/ReactFiberConfigNative.js @@ -32,6 +32,9 @@ import { } from 'react-reconciler/src/ReactEventPriorities'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import type {ReactContext} from 'shared/ReactTypes'; + import { getInspectorDataForViewTag, getInspectorDataForViewAtPoint, @@ -561,6 +564,14 @@ export function waitForCommitToBeReady(): null { } export const NotPendingTransition: TransitionStatus = null; +export const HostTransitionContext: ReactContext = { + $$typeof: REACT_CONTEXT_TYPE, + Provider: (null: any), + Consumer: (null: any), + _currentValue: NotPendingTransition, + _currentValue2: NotPendingTransition, + _threadCount: 0, +}; export type FormInstance = Instance; export function resetFormInstance(form: Instance): void {} diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index eae5dc5a964ff..34176bb83b5c9 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -178,6 +178,7 @@ import { isPrimaryRenderer, getResource, createHoistableInstance, + HostTransitionContext, } from './ReactFiberConfig'; import type {SuspenseInstance} from './ReactFiberConfig'; import {shouldError, shouldSuspend} from './ReactFiberReconciler'; @@ -185,7 +186,6 @@ import { pushHostContext, pushHostContainer, getRootHostContainer, - HostTransitionContext, } from './ReactFiberHostContext'; import { suspenseStackCursor, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index 78c9c8a9e05ff..363646eb7ddb4 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -28,6 +28,7 @@ import type {Flags} from './ReactFiberFlags'; import type {TransitionStatus} from './ReactFiberConfig'; import { + HostTransitionContext, NotPendingTransition as NoPendingHostTransition, setCurrentUpdatePriority, getCurrentUpdatePriority, @@ -156,7 +157,6 @@ import { peekEntangledActionThenable, chainThenableValue, } from './ReactFiberAsyncAction'; -import {HostTransitionContext} from './ReactFiberHostContext'; import {requestTransitionLane} from './ReactFiberRootScheduler'; import {isCurrentTreeHidden} from './ReactFiberHiddenContext'; import {requestCurrentTransition} from './ReactFiberTransition'; @@ -3276,8 +3276,7 @@ function useHostTransitionStatus(): TransitionStatus { if (!enableAsyncActions) { throw new Error('Not implemented.'); } - const status: TransitionStatus | null = readContext(HostTransitionContext); - return status !== null ? status : NoPendingHostTransition; + return readContext(HostTransitionContext); } function mountId(): string { diff --git a/packages/react-reconciler/src/ReactFiberHostContext.js b/packages/react-reconciler/src/ReactFiberHostContext.js index f930799f56387..a3b525b1a0bf0 100644 --- a/packages/react-reconciler/src/ReactFiberHostContext.js +++ b/packages/react-reconciler/src/ReactFiberHostContext.js @@ -9,21 +9,17 @@ import type {Fiber} from './ReactInternalTypes'; import type {StackCursor} from './ReactFiberStack'; -import type { - Container, - HostContext, - TransitionStatus, -} from './ReactFiberConfig'; +import type {Container, HostContext} from './ReactFiberConfig'; import type {Hook} from './ReactFiberHooks'; -import type {ReactContext} from 'shared/ReactTypes'; import { getChildHostContext, getRootHostContext, + HostTransitionContext, + NotPendingTransition, isPrimaryRenderer, } from './ReactFiberConfig'; import {createCursor, push, pop} from './ReactFiberStack'; -import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import {enableAsyncActions} from 'shared/ReactFeatureFlags'; const contextStackCursor: StackCursor = createCursor(null); @@ -38,21 +34,6 @@ const rootInstanceStackCursor: StackCursor = const hostTransitionProviderCursor: StackCursor = createCursor(null); -// TODO: This should initialize to NotPendingTransition, a constant -// imported from the fiber config. However, because of a cycle in the module -// graph, that value isn't defined during this module's initialization. I can't -// think of a way to work around this without moving that value out of the -// fiber config. For now, the "no provider" case is handled when reading, -// inside useHostTransitionStatus. -export const HostTransitionContext: ReactContext = { - $$typeof: REACT_CONTEXT_TYPE, - Provider: (null: any), - Consumer: (null: any), - _currentValue: null, - _currentValue2: null, - _threadCount: 0, -}; - function requiredContext(c: Value | null): Value { if (__DEV__) { if (c === null) { @@ -150,13 +131,13 @@ function popHostContext(fiber: Fiber): void { pop(hostTransitionProviderCursor, fiber); // When popping the transition provider, we reset the context value back - // to `null`. We can do this because you're not allowd to nest forms. If + // to `NotPendingTransition`. We can do this because you're not allowed to nest forms. If // we allowed for multiple nested host transition providers, then we'd // need to reset this to the parent provider's status. if (isPrimaryRenderer) { - HostTransitionContext._currentValue = null; + HostTransitionContext._currentValue = NotPendingTransition; } else { - HostTransitionContext._currentValue2 = null; + HostTransitionContext._currentValue2 = NotPendingTransition; } } } diff --git a/packages/react-reconciler/src/ReactFiberNewContext.js b/packages/react-reconciler/src/ReactFiberNewContext.js index cc908167e1e4f..57004e499bc28 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.js @@ -20,7 +20,7 @@ import type {SharedQueue} from './ReactFiberClassUpdateQueue'; import type {TransitionStatus} from './ReactFiberConfig'; import type {Hook} from './ReactFiberHooks'; -import {isPrimaryRenderer} from './ReactFiberConfig'; +import {isPrimaryRenderer, HostTransitionContext} from './ReactFiberConfig'; import {createCursor, push, pop} from './ReactFiberStack'; import { ContextProvider, @@ -48,10 +48,7 @@ import { enableAsyncActions, enableRenderableContext, } from 'shared/ReactFeatureFlags'; -import { - getHostTransitionProvider, - HostTransitionContext, -} from './ReactFiberHostContext'; +import {getHostTransitionProvider} from './ReactFiberHostContext'; import isArray from '../../shared/isArray'; import {enableContextProfiling} from '../../shared/ReactFeatureFlags'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js index faafe7159e25a..0826f9d95ee0d 100644 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberConfig.custom.js @@ -83,6 +83,7 @@ export const startSuspendingCommit = $$$config.startSuspendingCommit; export const suspendInstance = $$$config.suspendInstance; export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady; export const NotPendingTransition = $$$config.NotPendingTransition; +export const HostTransitionContext = $$$config.HostTransitionContext; export const resetFormInstance = $$$config.resetFormInstance; export const bindToConsole = $$$config.bindToConsole; diff --git a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js index 1bb04e8d32130..9d62e8a19ccf2 100644 --- a/packages/react-test-renderer/src/ReactFiberConfigTestHost.js +++ b/packages/react-test-renderer/src/ReactFiberConfigTestHost.js @@ -7,7 +7,10 @@ * @flow */ +import type {ReactContext} from 'shared/ReactTypes'; + import isArray from 'shared/isArray'; +import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; import { DefaultEventPriority, NoEventPriority, @@ -352,6 +355,14 @@ export function waitForCommitToBeReady(): null { } export const NotPendingTransition: TransitionStatus = null; +export const HostTransitionContext: ReactContext = { + $$typeof: REACT_CONTEXT_TYPE, + Provider: (null: any), + Consumer: (null: any), + _currentValue: NotPendingTransition, + _currentValue2: NotPendingTransition, + _threadCount: 0, +}; export type FormInstance = Instance; export function resetFormInstance(form: Instance): void {}