Skip to content

Commit 8f4feb9

Browse files
committed
useFormState -> useActionState
Kept the transform support since useFormState still exists.
1 parent 27e94af commit 8f4feb9

File tree

18 files changed

+58
-39
lines changed

18 files changed

+58
-39
lines changed

docs/02-app/01-building-your-application/02-data-fetching/02-server-actions-and-mutations.mdx

+10-10
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,10 @@ export default async function createsUser(formData) {
358358
}
359359
```
360360

361-
Once the fields have been validated on the server, you can return a serializable object in your action and use the React [`useFormState`](https://react.dev/reference/react-dom/hooks/useFormState) hook to show a message to the user.
361+
Once the fields have been validated on the server, you can return a serializable object in your action and use the React [`useActionState`](https://react.dev/reference/react-dom/hooks/useActionState) hook to show a message to the user.
362362

363-
- By passing the action to `useFormState`, the action's function signature changes to receive a new `prevState` or `initialState` parameter as its first argument.
364-
- `useFormState` is a React hook and therefore must be used in a Client Component.
363+
- By passing the action to `useActionState`, the action's function signature changes to receive a new `prevState` or `initialState` parameter as its first argument.
364+
- `useActionState` is a React hook and therefore must be used in a Client Component.
365365

366366
```tsx filename="app/actions.ts" switcher
367367
'use server'
@@ -385,20 +385,20 @@ export async function createUser(prevState, formData) {
385385
}
386386
```
387387

388-
Then, you can pass your action to the `useFormState` hook and use the returned `state` to display an error message.
388+
Then, you can pass your action to the `useActionState` hook and use the returned `state` to display an error message.
389389

390390
```tsx filename="app/ui/signup.tsx" switcher
391391
'use client'
392392

393-
import { useFormState } from 'react-dom'
393+
import { useActionState } from 'react'
394394
import { createUser } from '@/app/actions'
395395

396396
const initialState = {
397397
message: '',
398398
}
399399

400400
export function Signup() {
401-
const [state, formAction] = useFormState(createUser, initialState)
401+
const [state, formAction] = useActionState(createUser, initialState)
402402

403403
return (
404404
<form action={formAction}>
@@ -417,15 +417,15 @@ export function Signup() {
417417
```jsx filename="app/ui/signup.js" switcher
418418
'use client'
419419

420-
import { useFormState } from 'react-dom'
420+
import { useActionState } from 'react'
421421
import { createUser } from '@/app/actions'
422422

423423
const initialState = {
424424
message: '',
425425
}
426426

427427
export function Signup() {
428-
const [state, formAction] = useFormState(createUser, initialState)
428+
const [state, formAction] = useActionState(createUser, initialState)
429429

430430
return (
431431
<form action={formAction}>
@@ -739,7 +739,7 @@ export async function createTodo(prevState, formData) {
739739
740740
> **Good to know:**
741741
>
742-
> - Aside from throwing the error, you can also return an object to be handled by `useFormState`. See [Server-side validation and error handling](#server-side-validation-and-error-handling).
742+
> - Aside from throwing the error, you can also return an object to be handled by `useActionState`. See [Server-side validation and error handling](#server-side-validation-and-error-handling).
743743
744744
### Revalidating data
745745
@@ -1002,5 +1002,5 @@ For more information on Server Actions, check out the following React docs:
10021002
- [`"use server"`](https://react.dev/reference/react/use-server)
10031003
- [`<form>`](https://react.dev/reference/react-dom/components/form)
10041004
- [`useFormStatus`](https://react.dev/reference/react-dom/hooks/useFormStatus)
1005-
- [`useFormState`](https://react.dev/reference/react-dom/hooks/useFormState)
1005+
- [`useActionState`](https://react.dev/reference/react-dom/hooks/useActionState)
10061006
- [`useOptimistic`](https://react.dev/reference/react/useOptimistic)

docs/02-app/01-building-your-application/09-authentication/index.mdx

+10-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ The examples on this page walk through basic username and password auth for educ
2929

3030
### Sign-up and login functionality
3131

32-
You can use the [`<form>`](https://react.dev/reference/react-dom/components/form) element with React's [Server Actions](/docs/app/building-your-application/rendering/server-components), [`useFormStatus()`](https://react.dev/reference/react-dom/hooks/useFormStatus), and [`useFormState()`](https://react.dev/reference/react-dom/hooks/useFormState) to capture user credentials, validate form fields, and call your Authentication Provider's API or database.
32+
You can use the [`<form>`](https://react.dev/reference/react-dom/components/form) element with React's [Server Actions](/docs/app/building-your-application/rendering/server-components), [`useFormStatus()`](https://react.dev/reference/react-dom/hooks/useFormStatus), and [`useActionState()`](https://react.dev/reference/react/useActionState) to capture user credentials, validate form fields, and call your Authentication Provider's API or database.
3333

3434
Since Server Actions always execute on the server, they provide a secure environment for handling authentication logic.
3535

@@ -200,16 +200,16 @@ export async function signup(state, formData) {
200200
}
201201
```
202202

203-
Back in your `<SignupForm />`, you can use React's `useFormState()` hook to display validation errors to the user:
203+
Back in your `<SignupForm />`, you can use React's `useActionState()` hook to display validation errors to the user:
204204

205205
```tsx filename="app/ui/signup-form.tsx" switcher highlight={7,15,21,27-36}
206206
'use client'
207207

208-
import { useFormState } from 'react-dom'
208+
import { useActionState } from 'react'
209209
import { signup } from '@/app/actions/auth'
210210

211211
export function SignupForm() {
212-
const [state, action] = useFormState(signup, undefined)
212+
const [state, action] = useActionState(signup, undefined)
213213

214214
return (
215215
<form action={action}>
@@ -248,11 +248,11 @@ export function SignupForm() {
248248
```jsx filename="app/ui/signup-form.js" switcher highlight={7,15,21,27-36}
249249
'use client'
250250

251-
import { useFormState } from 'react-dom'
251+
import { useActionState } from 'react'
252252
import { signup } from '@/app/actions/auth'
253253

254254
export function SignupForm() {
255-
const [state, action] = useFormState(signup, undefined)
255+
const [state, action] = useActionState(signup, undefined)
256256

257257
return (
258258
<form action={action}>
@@ -293,7 +293,8 @@ You can also use the `useFormStatus()` hook to handle the pending state on form
293293
```tsx filename="app/ui/signup-form.tsx" highlight={7} switcher
294294
'use client'
295295

296-
import { useFormStatus, useFormState } from 'react-dom'
296+
import { useActionState } from 'react'
297+
import { useFormStatus } from 'react-dom'
297298

298299
export function SignupButton() {
299300
const { pending } = useFormStatus()
@@ -309,7 +310,8 @@ export function SignupButton() {
309310
```jsx filename="app/ui/signup-form.js" highlight={7} switcher
310311
'use client'
311312

312-
import { useFormStatus, useFormState } from 'react-dom'
313+
import { useActionState } from 'react'
314+
import { useFormStatus } from 'react-dom'
313315

314316
export function SignupButton() {
315317
const { pending } = useFormStatus()

examples/next-forms/app/add-form.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

3-
import { useFormState, useFormStatus } from "react-dom";
3+
import { useActionState } from 'react'
4+
import { useFormStatus } from 'react-dom'
45
import { createTodo } from "@/app/actions";
56

67
const initialState = {
@@ -18,7 +19,7 @@ function SubmitButton() {
1819
}
1920

2021
export function AddForm() {
21-
const [state, formAction] = useFormState(createTodo, initialState);
22+
const [state, formAction] = useActionState(createTodo, initialState);
2223

2324
return (
2425
<form action={formAction}>

examples/next-forms/app/delete-form.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

3-
import { useFormState, useFormStatus } from "react-dom";
3+
import { useActionState } from 'react'
4+
import { useFormStatus } from 'react-dom'
45
import { deleteTodo } from "@/app/actions";
56

67
const initialState = {
@@ -18,7 +19,7 @@ function DeleteButton() {
1819
}
1920

2021
export function DeleteForm({ id, todo }: { id: number; todo: string }) {
21-
const [state, formAction] = useFormState(deleteTodo, initialState);
22+
const [state, formAction] = useActionState(deleteTodo, initialState);
2223

2324
return (
2425
<form action={formAction}>

examples/with-fauna/components/EntryForm.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import cn from "classnames";
44
import { createEntryAction } from "@/actions/entry";
55
// @ts-ignore
6-
import { useFormState, useFormStatus } from "react-dom";
6+
import { useActionState } from 'react'
7+
// @ts-ignore
8+
import { useFormStatus } from 'react-dom'
79
import LoadingSpinner from "@/components/LoadingSpinner";
810
import SuccessMessage from "@/components/SuccessMessage";
911
import ErrorMessage from "@/components/ErrorMessage";
@@ -20,7 +22,7 @@ const initialState = {
2022
};
2123

2224
export default function EntryForm() {
23-
const [state, formAction] = useFormState(createEntryAction, initialState);
25+
const [state, formAction] = useActionState(createEntryAction, initialState);
2426
const { pending } = useFormStatus();
2527

2628
return (

packages/next-swc/crates/next-custom-transforms/src/transforms/react_server_components.rs

+1
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ impl ReactServerComponentValidator {
506506
"useSyncExternalStore",
507507
"useTransition",
508508
"useOptimistic",
509+
"useActionState"
509510
],
510511
),
511512
(

packages/next-swc/crates/next-custom-transforms/tests/errors/react-server-components/server-graph/react-dom-api/input.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { flushSync, unstable_batchedUpdates } from 'react-dom'
22

3+
import { useActionState } from 'react'
4+
35
import { useFormStatus, useFormState } from 'react-dom'
46

57
export default function () {

packages/next-swc/crates/next-custom-transforms/tests/errors/react-server-components/server-graph/react-dom-api/output.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { flushSync, unstable_batchedUpdates } from 'react-dom';
2+
import { useActionState } from 'react'
23
import { useFormStatus, useFormState } from 'react-dom';
34
export default function() {
45
return null;

packages/next/src/server/typescript/constant.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const DISALLOWED_SERVER_REACT_APIS: string[] = [
4040
'createFactory',
4141
'experimental_useOptimistic',
4242
'useOptimistic',
43+
'useActionState',
4344
]
4445

4546
export const DISALLOWED_SERVER_REACT_DOM_APIS: string[] = [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { useActionState } from 'react'
2+
3+
console.log({ useActionState })
4+
5+
export default function Page() {
6+
return null
7+
}

test/development/acceptance-app/rsc-build-errors.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ describe('Error overlay - RSC build errors', () => {
262262
'useSyncExternalStore',
263263
'useTransition',
264264
'useOptimistic',
265+
'useActionState',
265266
]
266267
for (const api of invalidReactServerApis) {
267268
it(`should error when ${api} from react is used in server component`, async () => {

test/e2e/app-dir/actions/app-action-form-state.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { nextTestSetup } from 'e2e-utils'
33
import { check } from 'next-test-utils'
44

5-
describe('app-dir action useFormState', () => {
5+
describe('app-dir action useActionState', () => {
66
const { next } = nextTestSetup({
77
files: __dirname,
88
dependencies: {

test/e2e/app-dir/actions/app/client/action-return-client-component/form.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
2-
import { useFormState } from 'react-dom'
2+
import { useActionState } from 'react'
33

44
export function Form({ action }) {
5-
const [state, formAction] = useFormState(action, null)
5+
const [state, formAction] = useActionState(action, null)
66
return (
77
<>
88
<form action={formAction}>

test/e2e/app-dir/actions/app/client/form-state/page-2/page.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use client'
22

3-
import { useFormState } from 'react-dom'
3+
import { useActionState } from 'react'
44
import { appendName } from '../actions'
55
import { useEffect, useState } from 'react'
66

77
export default function Page() {
8-
const [state, appendNameFormAction] = useFormState(
8+
const [state, appendNameFormAction] = useActionState(
99
appendName,
1010
'initial-state',
1111
'/client/form-state'

test/e2e/app-dir/actions/app/client/form-state/page.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use client'
22

3-
import { useFormState } from 'react-dom'
3+
import { useActionState } from 'react'
44
import { appendName } from './actions'
55
import { useEffect, useState } from 'react'
66

77
export default function Page() {
8-
const [state, appendNameFormAction] = useFormState(
8+
const [state, appendNameFormAction] = useActionState(
99
appendName,
1010
'initial-state',
1111
'/client/form-state'

test/e2e/app-dir/actions/app/server/action-return-client-component/form.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
2-
import { useFormState } from 'react-dom'
2+
import { useActionState } from 'react'
33

44
export function Form({ action }) {
5-
const [state, formAction] = useFormState(action, null)
5+
const [state, formAction] = useActionState(action, null)
66
return (
77
<>
88
<form action={formAction}>

test/e2e/app-dir/navigation/app/popstate-revalidate/foo/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
'use client'
22

3-
import { useFormState } from 'react-dom'
3+
import { useActionState } from 'react'
44
import { action } from './action'
55

66
export default function Page() {
7-
const [submitted, formAction] = useFormState(action, false)
7+
const [submitted, formAction] = useActionState(action, false)
88
if (submitted) {
99
return <div>Form Submitted.</div>
1010
}

test/turbopack-build-tests-manifest.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@
9595
"test/e2e/app-dir/actions/app-action-form-state.test.ts": {
9696
"passed": [],
9797
"failed": [
98-
"app-dir action useFormState should send the action to the provided permalink with form state when JS disabled",
99-
"app-dir action useFormState should support hydrating the app from progressively enhanced form request",
100-
"app-dir action useFormState should support submitting form state with JS",
101-
"app-dir action useFormState should support submitting form state without JS"
98+
"app-dir action useActionState should send the action to the provided permalink with form state when JS disabled",
99+
"app-dir action useActionState should support hydrating the app from progressively enhanced form request",
100+
"app-dir action useActionState should support submitting form state with JS",
101+
"app-dir action useActionState should support submitting form state without JS"
102102
],
103103
"pending": [],
104104
"flakey": [],

0 commit comments

Comments
 (0)