Skip to content

Commit 10f66e9

Browse files
acdliteAndyPengc12
authored andcommitted
[useFormState] Allow sync actions (facebook#27571)
Updates useFormState to allow a sync function to be passed as an action. A form action is almost always async, because it needs to talk to the server. But since we support client-side actions, too, there's no reason we can't allow sync actions, too. I originally chose not to allow them to keep the implementation simpler but it's not really that much more complicated because we already support this for actions passed to startTransition. So now it's consistent: anywhere an action is accepted, a sync client function is a valid input.
1 parent 49d237b commit 10f66e9

File tree

31 files changed

+667
-471
lines changed

31 files changed

+667
-471
lines changed

fixtures/flight/config/modules.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@ function getModules() {
108108
// TypeScript project and set up the config
109109
// based on tsconfig.json
110110
if (hasTsConfig) {
111-
const ts = require(resolve.sync('typescript', {
112-
basedir: paths.appNodeModules,
113-
}));
111+
const ts = require(
112+
resolve.sync('typescript', {
113+
basedir: paths.appNodeModules,
114+
})
115+
);
114116
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
115117
// Otherwise we'll check if there is jsconfig.json
116118
// for non TS projects.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@
8181
"minimist": "^1.2.3",
8282
"mkdirp": "^0.5.1",
8383
"ncp": "^2.0.0",
84-
"prettier": "2.8.3",
84+
"prettier": "3.0.3",
8585
"pretty-format": "^29.4.1",
8686
"prop-types": "^15.6.2",
8787
"random-seed": "^0.3.0",
8888
"react-lifecycles-compat": "^3.0.4",
8989
"rimraf": "^3.0.0",
9090
"rollup": "^3.17.1",
91-
"rollup-plugin-prettier": "^3.0.0",
91+
"rollup-plugin-prettier": "^4.1.1",
9292
"rollup-plugin-strip-banner": "^3.0.0",
9393
"semver": "^7.1.1",
9494
"signedsource": "^2.0.0",

packages/react-client/src/ReactFlightClient.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -572,11 +572,11 @@ function createServerReferenceProxy<A: Iterable<any>, T>(
572572
}
573573
// Since this is a fake Promise whose .then doesn't chain, we have to wrap it.
574574
// TODO: Remove the wrapper once that's fixed.
575-
return ((Promise.resolve(p): any): Promise<Array<any>>).then(function (
576-
bound,
577-
) {
578-
return callServer(metaData.id, bound.concat(args));
579-
});
575+
return ((Promise.resolve(p): any): Promise<Array<any>>).then(
576+
function (bound) {
577+
return callServer(metaData.id, bound.concat(args));
578+
},
579+
);
580580
};
581581
registerServerReference(proxy, metaData);
582582
return proxy;

packages/react-devtools-shared/src/__tests__/profilerChangeDescriptions-test.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,11 @@ describe('Profiler change descriptions', () => {
4040
}
4141

4242
const MemoizedChild = React.memo(Child, areEqual);
43-
const ForwardRefChild = React.forwardRef(function RefForwardingComponent(
44-
props,
45-
ref,
46-
) {
47-
return <Child />;
48-
});
43+
const ForwardRefChild = React.forwardRef(
44+
function RefForwardingComponent(props, ref) {
45+
return <Child />;
46+
},
47+
);
4948

5049
let forceUpdate = null;
5150

packages/react-devtools-shared/src/devtools/ProfilingCache.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@ export default class ProfilingCache {
3333
this._profilerStore = profilerStore;
3434
}
3535

36-
getCommitTree: ({
37-
commitIndex: number,
38-
rootID: number,
39-
}) => CommitTree = ({commitIndex, rootID}) =>
36+
getCommitTree: ({commitIndex: number, rootID: number}) => CommitTree = ({
37+
commitIndex,
38+
rootID,
39+
}) =>
4040
getCommitTree({
4141
commitIndex,
4242
profilerStore: this._profilerStore,
4343
rootID,
4444
});
4545

46-
getFiberCommits: ({
47-
fiberID: number,
48-
rootID: number,
49-
}) => Array<number> = ({fiberID, rootID}) => {
46+
getFiberCommits: ({fiberID: number, rootID: number}) => Array<number> = ({
47+
fiberID,
48+
rootID,
49+
}) => {
5050
const cachedFiberCommits = this._fiberCommits.get(fiberID);
5151
if (cachedFiberCommits != null) {
5252
return cachedFiberCommits;

packages/react-devtools-shared/src/hooks/__tests__/parseHookNames-test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ describe('parseHookNames', () => {
7070
const hooksList = flattenHooksList(hooksTree);
7171

7272
// Runs in the UI thread so it can share Network cache:
73-
const locationKeyToHookSourceAndMetadata = await loadSourceAndMetadata(
74-
hooksList,
75-
);
73+
const locationKeyToHookSourceAndMetadata =
74+
await loadSourceAndMetadata(hooksList);
7675

7776
// Runs in a Worker because it's CPU intensive:
7877
return parseSourceAndMetadata(

packages/react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ if (document.body != null) {
2626
installFizzInstrObserver(document.body);
2727
}
2828
// $FlowFixMe[incompatible-cast]
29-
handleExistingNodes((document.body /*: HTMLElement */));
29+
handleExistingNodes((document.body: HTMLElement));
3030
} else {
3131
// Document must be loading -- body may not exist yet if the fizz external
3232
// runtime is sent in <head> (e.g. as a preinit resource)
@@ -38,7 +38,7 @@ if (document.body != null) {
3838
installFizzInstrObserver(document.body);
3939
}
4040
// $FlowFixMe[incompatible-cast]
41-
handleExistingNodes((document.body /*: HTMLElement */));
41+
handleExistingNodes((document.body: HTMLElement));
4242

4343
// We can call disconnect without takeRecord here,
4444
// since we only expect a single document.body
@@ -49,15 +49,15 @@ if (document.body != null) {
4949
domBodyObserver.observe(document.documentElement, {childList: true});
5050
}
5151

52-
function handleExistingNodes(target /*: HTMLElement */) {
52+
function handleExistingNodes(target: HTMLElement) {
5353
const existingNodes = target.querySelectorAll('template');
5454
for (let i = 0; i < existingNodes.length; i++) {
5555
handleNode(existingNodes[i]);
5656
}
5757
}
5858

59-
function installFizzInstrObserver(target /*: Node */) {
60-
const handleMutations = (mutations /*: Array<MutationRecord> */) => {
59+
function installFizzInstrObserver(target: Node) {
60+
const handleMutations = (mutations: Array<MutationRecord>) => {
6161
for (let i = 0; i < mutations.length; i++) {
6262
const addedNodes = mutations[i].addedNodes;
6363
for (let j = 0; j < addedNodes.length; j++) {
@@ -80,13 +80,13 @@ function installFizzInstrObserver(target /*: Node */) {
8080
});
8181
}
8282

83-
function handleNode(node_ /*: Node */) {
83+
function handleNode(node_: Node) {
8484
// $FlowFixMe[incompatible-cast]
85-
if (node_.nodeType !== 1 || !(node_ /*: HTMLElement */).dataset) {
85+
if (node_.nodeType !== 1 || !(node_: HTMLElement).dataset) {
8686
return;
8787
}
8888
// $FlowFixMe[incompatible-cast]
89-
const node = (node_ /*: HTMLElement */);
89+
const node = (node_: HTMLElement);
9090
const dataset = node.dataset;
9191
if (dataset['rxi'] != null) {
9292
clientRenderBoundary(

packages/react-dom-bindings/src/shared/ReactDOMFormActions.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11+
import type {Awaited} from 'shared/ReactTypes';
1112

1213
import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags';
1314
import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -76,10 +77,10 @@ export function useFormStatus(): FormStatus {
7677
}
7778

7879
export function useFormState<S, P>(
79-
action: (S, P) => Promise<S>,
80-
initialState: S,
80+
action: (Awaited<S>, P) => S,
81+
initialState: Awaited<S>,
8182
permalink?: string,
82-
): [S, (P) => void] {
83+
): [Awaited<S>, (P) => void] {
8384
if (!(enableFormActions && enableAsyncActions)) {
8485
throw new Error('Not implemented.');
8586
} else {

packages/react-dom/index.experimental.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export {
3131
version,
3232
} from './src/client/ReactDOM';
3333

34+
import type {Awaited} from 'shared/ReactTypes';
3435
import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
3536
import {useFormStatus, useFormState} from './src/client/ReactDOM';
3637

@@ -45,10 +46,10 @@ export function experimental_useFormStatus(): FormStatus {
4546
}
4647

4748
export function experimental_useFormState<S, P>(
48-
action: (S, P) => Promise<S>,
49-
initialState: S,
49+
action: (Awaited<S>, P) => S,
50+
initialState: Awaited<S>,
5051
permalink?: string,
51-
): [S, (P) => void] {
52+
): [Awaited<S>, (P) => void] {
5253
if (__DEV__) {
5354
console.error(
5455
'useFormState is now in canary. Remove the experimental_ prefix. ' +

packages/react-dom/server-rendering-stub.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
useFormStatus,
3535
useFormState,
3636
} from './src/server/ReactDOMServerRenderingStub';
37+
import type {Awaited} from 'shared/ReactTypes';
3738

3839
export function experimental_useFormStatus(): FormStatus {
3940
if (__DEV__) {
@@ -46,10 +47,10 @@ export function experimental_useFormStatus(): FormStatus {
4647
}
4748

4849
export function experimental_useFormState<S, P>(
49-
action: (S, P) => Promise<S>,
50-
initialState: S,
50+
action: (Awaited<S>, P) => S,
51+
initialState: Awaited<S>,
5152
permalink?: string,
52-
): [S, (P) => void] {
53+
): [Awaited<S>, (P) => void] {
5354
if (__DEV__) {
5455
console.error(
5556
'useFormState is now in canary. Remove the experimental_ prefix. ' +

0 commit comments

Comments
 (0)