Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f58fe2a

Browse files
committedApr 25, 2023
Parameterize pending state type of host transitions
Updates the internals of async form actions so they can use a custom pending state type, instead of a boolean. I'm not sure this is how we'll end up doing it once optimistic state is implemented, but it fits with how we handle the isPending state of useTransition. The next step is to connect this to useFormStatus, which will read the value of the nearest pending form state using context.
1 parent fc3b44c commit f58fe2a

File tree

10 files changed

+61
-20
lines changed

10 files changed

+61
-20
lines changed
 

‎packages/react-art/src/ReactFiberConfigART.js

+2
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,5 @@ export function suspendInstance(type, props) {}
479479
export function waitForCommitToBeReady() {
480480
return null;
481481
}
482+
483+
export const NotPendingTransition = null;

‎packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

+8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import type {
1818
} from 'react-reconciler/src/ReactTestSelectors';
1919
import type {ReactScopeInstance} from 'shared/ReactTypes';
2020
import type {AncestorInfoDev} from './validateDOMNesting';
21+
import type {FormStatus} from 'react-dom/src/ReactDOMFormActions';
2122

23+
// TODO: For some reason the build script fails if this file doesn't include the
24+
// extension. I do not know why.
25+
import {NotPending} from 'react-dom/src/ReactDOMFormActions.js';
2226
import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
2327
import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
2428
// TODO: Remove this deep import when we delete the legacy root API
@@ -164,6 +168,8 @@ export type TimeoutHandle = TimeoutID;
164168
export type NoTimeout = -1;
165169
export type RendererInspectionConfig = $ReadOnly<{}>;
166170

171+
export type TransitionStatus = FormStatus;
172+
167173
type SelectionInformation = {
168174
focusedElem: null | HTMLElement,
169175
selectionRange: mixed,
@@ -3431,3 +3437,5 @@ function insertStylesheetIntoRoot(
34313437
}
34323438
resource.state.loading |= Inserted;
34333439
}
3440+
3441+
export const NotPendingTransition: TransitionStatus = NotPending;

‎packages/react-dom-bindings/src/events/plugins/FormActionEventPlugin.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {DOMEventName} from '../DOMEventNames';
1212
import type {DispatchQueue} from '../DOMPluginEventSystem';
1313
import type {EventSystemFlags} from '../EventSystemFlags';
1414
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15+
import type {FormStatus} from 'react-dom/src/ReactDOMFormActions';
1516

1617
import {getFiberCurrentPropsFromNode} from '../../client/ReactDOMComponentTree';
1718
import {startHostTransition} from 'react-reconciler/src/ReactFiberReconciler';
@@ -98,7 +99,16 @@ function extractEvents(
9899
formData = new FormData(form);
99100
}
100101

101-
startHostTransition(formInst, action, formData);
102+
const pendingState: FormStatus = {
103+
pending: true,
104+
data: formData,
105+
method: form.method,
106+
action: action,
107+
};
108+
if (__DEV__) {
109+
Object.freeze(pendingState);
110+
}
111+
startHostTransition(formInst, pendingState, action, formData);
102112
}
103113

104114
dispatchQueue.push({

‎packages/react-dom/src/ReactDOMFormActions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const sharedNotPendingObject = {
3434
action: null,
3535
};
3636

37-
const NotPending: FormStatus = __DEV__
37+
export const NotPending: FormStatus = __DEV__
3838
? Object.freeze(sharedNotPendingObject)
3939
: sharedNotPendingObject;
4040

‎packages/react-native-renderer/src/ReactFiberConfigFabric.js

+3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export type UpdatePayload = Object;
9090

9191
export type TimeoutHandle = TimeoutID;
9292
export type NoTimeout = -1;
93+
export type TransitionStatus = mixed;
9394

9495
export type RendererInspectionConfig = $ReadOnly<{
9596
// Deprecated. Replaced with getInspectorDataForViewAtPoint.
@@ -489,3 +490,5 @@ export function suspendInstance(type: Type, props: Props): void {}
489490
export function waitForCommitToBeReady(): null {
490491
return null;
491492
}
493+
494+
export const NotPendingTransition: TransitionStatus = null;

‎packages/react-native-renderer/src/ReactFiberConfigNative.js

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type ChildSet = void; // Unused
4343

4444
export type TimeoutHandle = TimeoutID;
4545
export type NoTimeout = -1;
46+
export type TransitionStatus = mixed;
4647

4748
export type RendererInspectionConfig = $ReadOnly<{
4849
// Deprecated. Replaced with getInspectorDataForViewAtPoint.
@@ -542,3 +543,5 @@ export function suspendInstance(type: Type, props: Props): void {}
542543
export function waitForCommitToBeReady(): null {
543544
return null;
544545
}
546+
547+
export const NotPendingTransition: TransitionStatus = null;

‎packages/react-noop-renderer/src/createReactNoop.js

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ type SuspenseyCommitSubscription = {
7878
commit: null | (() => void),
7979
};
8080

81+
export type TransitionStatus = mixed;
82+
8183
const NO_CONTEXT = {};
8284
const UPPERCASE_CONTEXT = {};
8385
const UPDATE_SIGNAL = {};
@@ -629,6 +631,8 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
629631
},
630632

631633
waitForCommitToBeReady,
634+
635+
NotPendingTransition: (null: TransitionStatus),
632636
};
633637

634638
const hostConfig = useMutation

‎packages/react-reconciler/src/ReactFiberHooks.js

+24-18
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import type {
2727
import type {Lanes, Lane} from './ReactFiberLane';
2828
import type {HookFlags} from './ReactHookEffectTags';
2929
import type {Flags} from './ReactFiberFlags';
30+
import type {TransitionStatus} from './ReactFiberConfig';
3031

32+
import {NotPendingTransition as NoPendingHostTransition} from './ReactFiberConfig';
3133
import ReactSharedInternals from 'shared/ReactSharedInternals';
3234
import {
3335
enableDebugTracing,
@@ -757,9 +759,9 @@ export function renderTransitionAwareHostComponentWithHooks(
757759
current: Fiber | null,
758760
workInProgress: Fiber,
759761
lanes: Lanes,
760-
): boolean {
762+
): TransitionStatus {
761763
if (!(enableFormActions && enableAsyncActions)) {
762-
return false;
764+
throw new Error('Not implemented.');
763765
}
764766
return renderWithHooks(
765767
current,
@@ -771,16 +773,19 @@ export function renderTransitionAwareHostComponentWithHooks(
771773
);
772774
}
773775

774-
export function TransitionAwareHostComponent(): boolean {
776+
export function TransitionAwareHostComponent(): TransitionStatus {
775777
if (!(enableFormActions && enableAsyncActions)) {
776-
return false;
778+
throw new Error('Not implemented.');
777779
}
778780
const dispatcher = ReactCurrentDispatcher.current;
779-
const [booleanOrThenable] = dispatcher.useState();
780-
return typeof booleanOrThenable === 'boolean'
781-
? booleanOrThenable
782-
: // This will suspend until the async action scope has finished.
783-
useThenable(booleanOrThenable);
781+
const [maybeThenable] = dispatcher.useState();
782+
if (typeof maybeThenable.then === 'function') {
783+
const thenable: Thenable<TransitionStatus> = (maybeThenable: any);
784+
return useThenable(thenable);
785+
} else {
786+
const status: TransitionStatus = maybeThenable;
787+
return status;
788+
}
784789
}
785790

786791
export function checkDidRenderIdHook(): boolean {
@@ -2520,6 +2525,7 @@ function startTransition<S>(
25202525

25212526
export function startHostTransition<F>(
25222527
formFiber: Fiber,
2528+
pendingState: TransitionStatus,
25232529
callback: F => mixed,
25242530
formData: F,
25252531
): void {
@@ -2551,24 +2557,24 @@ export function startHostTransition<F>(
25512557
// Create the state hook used by TransitionAwareHostComponent. This is
25522558
// essentially an inlined version of mountState.
25532559
const queue: UpdateQueue<
2554-
Thenable<boolean> | boolean,
2555-
Thenable<boolean> | boolean,
2560+
Thenable<TransitionStatus> | TransitionStatus,
2561+
Thenable<TransitionStatus> | TransitionStatus,
25562562
> = {
25572563
pending: null,
25582564
lanes: NoLanes,
25592565
dispatch: null,
25602566
lastRenderedReducer: basicStateReducer,
2561-
lastRenderedState: false,
2567+
lastRenderedState: NoPendingHostTransition,
25622568
};
25632569
const stateHook: Hook = {
2564-
memoizedState: false,
2565-
baseState: false,
2570+
memoizedState: NoPendingHostTransition,
2571+
baseState: NoPendingHostTransition,
25662572
baseQueue: null,
25672573
queue: queue,
25682574
next: null,
25692575
};
25702576

2571-
const dispatch: (Thenable<boolean> | boolean) => void =
2577+
const dispatch: (Thenable<TransitionStatus> | TransitionStatus) => void =
25722578
(dispatchSetState.bind(null, formFiber, queue): any);
25732579
setPending = queue.dispatch = dispatch;
25742580

@@ -2582,14 +2588,14 @@ export function startHostTransition<F>(
25822588
} else {
25832589
// This fiber was already upgraded to be stateful.
25842590
const stateHook: Hook = formFiber.memoizedState;
2585-
const dispatch: (Thenable<boolean> | boolean) => void =
2591+
const dispatch: (Thenable<TransitionStatus> | TransitionStatus) => void =
25862592
stateHook.queue.dispatch;
25872593
setPending = dispatch;
25882594
}
25892595

25902596
startTransition(
2591-
true,
2592-
false,
2597+
pendingState,
2598+
NoPendingHostTransition,
25932599
setPending,
25942600
// TODO: We can avoid this extra wrapper, somehow. Figure out layering
25952601
// once more of this function is implemented.

‎packages/react-reconciler/src/forks/ReactFiberConfig.custom.js

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export opaque type ChildSet = mixed; // eslint-disable-line no-undef
3838
export opaque type TimeoutHandle = mixed; // eslint-disable-line no-undef
3939
export opaque type NoTimeout = mixed; // eslint-disable-line no-undef
4040
export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef
41+
export opaque type TransitionStatus = mixed; // eslint-disable-line no-undef
4142
export type EventResponder = any;
4243

4344
export const getPublicInstance = $$$config.getPublicInstance;
@@ -75,6 +76,7 @@ export const preloadInstance = $$$config.preloadInstance;
7576
export const startSuspendingCommit = $$$config.startSuspendingCommit;
7677
export const suspendInstance = $$$config.suspendInstance;
7778
export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady;
79+
export const NotPendingTransition = $$$config.waitForCommitToBeReady;
7880

7981
// -------------------
8082
// Microtasks

‎packages/react-test-renderer/src/ReactFiberConfigTestHost.js

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type NoTimeout = -1;
4141
export type EventResponder = any;
4242

4343
export type RendererInspectionConfig = $ReadOnly<{}>;
44+
export type TransitionStatus = mixed;
4445

4546
export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
4647
export * from 'react-reconciler/src/ReactFiberConfigWithNoHydration';
@@ -343,3 +344,5 @@ export function suspendInstance(type: Type, props: Props): void {}
343344
export function waitForCommitToBeReady(): null {
344345
return null;
345346
}
347+
348+
export const NotPendingTransition: TransitionStatus = null;

0 commit comments

Comments
 (0)
Please sign in to comment.