91
StoreConsistency,
92
MountLayoutDev as MountLayoutDevEffect,
93
MountPassiveDev as MountPassiveDevEffect,
94
+ FormReset,
95
} from './ReactFiberFlags';
96
import {
97
HasEffect as HookHasEffect,
845
if (!enableAsyncActions) {
846
throw new Error('Not implemented.');
847
}
848
+
849
const dispatcher: any = ReactSharedInternals.H;
850
const [maybeThenable] = dispatcher.useState();
851
+ let nextState;
852
if (typeof maybeThenable.then === 'function') {
853
const thenable: Thenable<TransitionStatus> = (maybeThenable: any);
851
- return useThenable(thenable);
854
+ nextState = useThenable(thenable);
855
} else {
856
const status: TransitionStatus = maybeThenable;
854
- return status;
857
+ nextState = status;
858
+ }
859
+
860
+ // The "reset state" is an object. If it changes, that means something
861
+ // requested that we reset the form.
862
+ const [nextResetState] = dispatcher.useState();
863
+ const prevResetState =
864
+ currentHook !== null ? currentHook.memoizedState : null;
865
+ if (prevResetState !== nextResetState) {
866
+ // Schedule a form reset
867
+ currentlyRenderingFiber.flags |= FormReset;
868
}
869
+
870
+ return nextState;
871
}
872
873
export function checkDidRenderIdHook(): boolean {
2963
next: null,
2964
};
2965
2951
- // Add the state hook to both fiber alternates. The idea is that the fiber
2966
+ // We use another state hook to track whether the form needs to be reset.
2967
+ // The state is an empty object. To trigger a reset, we update the state
2968
+ // to a new object. Then during rendering, we detect that the state has
2969
+ // changed and schedule a commit effect.
2970
+ const initialResetState = {};
2971
+ const newResetStateQueue: UpdateQueue<Object, Object> = {
2972
+ pending: null,
2973
+ lanes: NoLanes,
2974
+ // We're going to cheat and intentionally not create a bound dispatch
2975
+ // method, because we can call it directly in startTransition.
2976
+ dispatch: (null: any),
2977
+ lastRenderedReducer: basicStateReducer,
2978
+ lastRenderedState: initialResetState,
2979
+ };
2980
+ const resetStateHook: Hook = {
2981
+ memoizedState: initialResetState,
2982
+ baseState: initialResetState,
2983
+ baseQueue: null,
2984
+ queue: newResetStateQueue,
2985
+ next: null,
2986
+ };
2987
+ stateHook.next = resetStateHook;
2988
+
2989
+ // Add the hook list to both fiber alternates. The idea is that the fiber
2990
// had this hook all along.
2991
formFiber.memoizedState = stateHook;
2992
const alternate = formFiber.alternate;
3006
NoPendingHostTransition,
3007
// TODO: We can avoid this extra wrapper, somehow. Figure out layering
3008
// once more of this function is implemented.
2971
- () => callback(formData),
3009
+ () => {
3010
+ // Automatically reset the form when the action completes.
3011
+ requestFormReset(formFiber);
3012
+ return callback(formData);
3013
+ },
3014
);
3015
}
3016
3017
+function requestFormReset(formFiber: Fiber) {
3018
+ const transition = requestCurrentTransition();
3019
+
3020
+ if (__DEV__) {
3021
+ if (transition === null) {
3022
+ // An optimistic update occurred, but startTransition is not on the stack.
3023
+ // The form reset will be scheduled at default (sync) priority, which
3024
+ // is probably not what the user intended. Most likely because the
3025
+ // requestFormReset call happened after an `await`.
3026
+ // TODO: Theoretically, requestFormReset is still useful even for
3027
+ // non-transition updates because it allows you to update defaultValue
3028
+ // synchronously and then wait to reset until after the update commits.
3029
+ // I've chosen to warn anyway because it's more likely the `await` mistake
3030
+ // described above. But arguably we shouldn't.
3031
+ console.error(
3032
+ 'requestFormReset was called outside a transition or action. To ' +
3033
+ 'fix, move to an action, or wrap with startTransition.',
3034
+ );
3035
+ }
3036
+ }
3037
+
3038
+ const newResetState = {};
3039
+ const resetStateHook: Hook = (formFiber.memoizedState.next: any);
3040
+ const resetStateQueue = resetStateHook.queue;
3041
+ dispatchSetState(formFiber, resetStateQueue, newResetState);
3042
+}
3043
+
3044
function mountTransition(): [
3045
boolean,
3046
(callback: () => void, options?: StartTransitionOptions) => void,