Skip to content

Commit

Permalink
[useFormState] Allow sync actions (facebook#27571)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
acdlite authored and AndyPengc12 committed Apr 15, 2024
1 parent 49d237b commit 10f66e9
Show file tree
Hide file tree
Showing 31 changed files with 667 additions and 471 deletions.
8 changes: 5 additions & 3 deletions fixtures/flight/config/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ function getModules() {
// TypeScript project and set up the config
// based on tsconfig.json
if (hasTsConfig) {
const ts = require(resolve.sync('typescript', {
basedir: paths.appNodeModules,
}));
const ts = require(
resolve.sync('typescript', {
basedir: paths.appNodeModules,
})
);
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
// Otherwise we'll check if there is jsconfig.json
// for non TS projects.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@
"minimist": "^1.2.3",
"mkdirp": "^0.5.1",
"ncp": "^2.0.0",
"prettier": "2.8.3",
"prettier": "3.0.3",
"pretty-format": "^29.4.1",
"prop-types": "^15.6.2",
"random-seed": "^0.3.0",
"react-lifecycles-compat": "^3.0.4",
"rimraf": "^3.0.0",
"rollup": "^3.17.1",
"rollup-plugin-prettier": "^3.0.0",
"rollup-plugin-prettier": "^4.1.1",
"rollup-plugin-strip-banner": "^3.0.0",
"semver": "^7.1.1",
"signedsource": "^2.0.0",
Expand Down
10 changes: 5 additions & 5 deletions packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -572,11 +572,11 @@ function createServerReferenceProxy<A: Iterable<any>, T>(
}
// Since this is a fake Promise whose .then doesn't chain, we have to wrap it.
// TODO: Remove the wrapper once that's fixed.
return ((Promise.resolve(p): any): Promise<Array<any>>).then(function (
bound,
) {
return callServer(metaData.id, bound.concat(args));
});
return ((Promise.resolve(p): any): Promise<Array<any>>).then(
function (bound) {
return callServer(metaData.id, bound.concat(args));
},
);
};
registerServerReference(proxy, metaData);
return proxy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ describe('Profiler change descriptions', () => {
}

const MemoizedChild = React.memo(Child, areEqual);
const ForwardRefChild = React.forwardRef(function RefForwardingComponent(
props,
ref,
) {
return <Child />;
});
const ForwardRefChild = React.forwardRef(
function RefForwardingComponent(props, ref) {
return <Child />;
},
);

let forceUpdate = null;

Expand Down
16 changes: 8 additions & 8 deletions packages/react-devtools-shared/src/devtools/ProfilingCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,20 @@ export default class ProfilingCache {
this._profilerStore = profilerStore;
}

getCommitTree: ({
commitIndex: number,
rootID: number,
}) => CommitTree = ({commitIndex, rootID}) =>
getCommitTree: ({commitIndex: number, rootID: number}) => CommitTree = ({
commitIndex,
rootID,
}) =>
getCommitTree({
commitIndex,
profilerStore: this._profilerStore,
rootID,
});

getFiberCommits: ({
fiberID: number,
rootID: number,
}) => Array<number> = ({fiberID, rootID}) => {
getFiberCommits: ({fiberID: number, rootID: number}) => Array<number> = ({
fiberID,
rootID,
}) => {
const cachedFiberCommits = this._fiberCommits.get(fiberID);
if (cachedFiberCommits != null) {
return cachedFiberCommits;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ describe('parseHookNames', () => {
const hooksList = flattenHooksList(hooksTree);

// Runs in the UI thread so it can share Network cache:
const locationKeyToHookSourceAndMetadata = await loadSourceAndMetadata(
hooksList,
);
const locationKeyToHookSourceAndMetadata =
await loadSourceAndMetadata(hooksList);

// Runs in a Worker because it's CPU intensive:
return parseSourceAndMetadata(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ if (document.body != null) {
installFizzInstrObserver(document.body);
}
// $FlowFixMe[incompatible-cast]
handleExistingNodes((document.body /*: HTMLElement */));
handleExistingNodes((document.body: HTMLElement));
} else {
// Document must be loading -- body may not exist yet if the fizz external
// runtime is sent in <head> (e.g. as a preinit resource)
Expand All @@ -38,7 +38,7 @@ if (document.body != null) {
installFizzInstrObserver(document.body);
}
// $FlowFixMe[incompatible-cast]
handleExistingNodes((document.body /*: HTMLElement */));
handleExistingNodes((document.body: HTMLElement));

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

function handleExistingNodes(target /*: HTMLElement */) {
function handleExistingNodes(target: HTMLElement) {
const existingNodes = target.querySelectorAll('template');
for (let i = 0; i < existingNodes.length; i++) {
handleNode(existingNodes[i]);
}
}

function installFizzInstrObserver(target /*: Node */) {
const handleMutations = (mutations /*: Array<MutationRecord> */) => {
function installFizzInstrObserver(target: Node) {
const handleMutations = (mutations: Array<MutationRecord>) => {
for (let i = 0; i < mutations.length; i++) {
const addedNodes = mutations[i].addedNodes;
for (let j = 0; j < addedNodes.length; j++) {
Expand All @@ -80,13 +80,13 @@ function installFizzInstrObserver(target /*: Node */) {
});
}

function handleNode(node_ /*: Node */) {
function handleNode(node_: Node) {
// $FlowFixMe[incompatible-cast]
if (node_.nodeType !== 1 || !(node_ /*: HTMLElement */).dataset) {
if (node_.nodeType !== 1 || !(node_: HTMLElement).dataset) {
return;
}
// $FlowFixMe[incompatible-cast]
const node = (node_ /*: HTMLElement */);
const node = (node_: HTMLElement);
const dataset = node.dataset;
if (dataset['rxi'] != null) {
clientRenderBoundary(
Expand Down
7 changes: 4 additions & 3 deletions packages/react-dom-bindings/src/shared/ReactDOMFormActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

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

import {enableAsyncActions, enableFormActions} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -76,10 +77,10 @@ export function useFormStatus(): FormStatus {
}

export function useFormState<S, P>(
action: (S, P) => Promise<S>,
initialState: S,
action: (Awaited<S>, P) => S,
initialState: Awaited<S>,
permalink?: string,
): [S, (P) => void] {
): [Awaited<S>, (P) => void] {
if (!(enableFormActions && enableAsyncActions)) {
throw new Error('Not implemented.');
} else {
Expand Down
7 changes: 4 additions & 3 deletions packages/react-dom/index.experimental.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export {
version,
} from './src/client/ReactDOM';

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

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

export function experimental_useFormState<S, P>(
action: (S, P) => Promise<S>,
initialState: S,
action: (Awaited<S>, P) => S,
initialState: Awaited<S>,
permalink?: string,
): [S, (P) => void] {
): [Awaited<S>, (P) => void] {
if (__DEV__) {
console.error(
'useFormState is now in canary. Remove the experimental_ prefix. ' +
Expand Down
7 changes: 4 additions & 3 deletions packages/react-dom/server-rendering-stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
useFormStatus,
useFormState,
} from './src/server/ReactDOMServerRenderingStub';
import type {Awaited} from 'shared/ReactTypes';

export function experimental_useFormStatus(): FormStatus {
if (__DEV__) {
Expand All @@ -46,10 +47,10 @@ export function experimental_useFormStatus(): FormStatus {
}

export function experimental_useFormState<S, P>(
action: (S, P) => Promise<S>,
initialState: S,
action: (Awaited<S>, P) => S,
initialState: Awaited<S>,
permalink?: string,
): [S, (P) => void] {
): [Awaited<S>, (P) => void] {
if (__DEV__) {
console.error(
'useFormState is now in canary. Remove the experimental_ prefix. ' +
Expand Down
Loading

0 comments on commit 10f66e9

Please sign in to comment.