diff --git a/src/content/reference/rsc/server-functions.md b/src/content/reference/rsc/server-functions.md index 25d2eca2dd4..4dfe7efb029 100644 --- a/src/content/reference/rsc/server-functions.md +++ b/src/content/reference/rsc/server-functions.md @@ -34,7 +34,7 @@ Server Functions can be created in Server Components and passed as props to Clie ## Usage {/*usage*/} -### Creating a Server Function from a Server Component {/*creating-a-server-function-from-a-server-component*/} +### Creating a Server Function in a Server Component {/*creating-a-server-function-from-a-server-component*/} Server Components can define Server Functions with the `"use server"` directive: @@ -42,7 +42,7 @@ Server Components can define Server Functions with the `"use server"` directive: // Server Component import Button from './Button'; -function EmptyNote () { +function EmptyNote() { async function createNoteAction() { // Server Function 'use server'; @@ -50,7 +50,7 @@ function EmptyNote () { await db.notes.create(); } - return + return ; } ``` For more, see the docs for [`"use server"`](/reference/rsc/use-server). - -### Importing Server Functions from Client Components {/*importing-server-functions-from-client-components*/} +### Importing a Server Function into a Client Component {/*importing-server-functions-from-client-components*/} Client Components can import Server Functions from files that use the `"use server"` directive: -```js [[1, 3, "createNote"]] +```js [[1, 4, "createNote"]] +// actions.js "use server"; export async function createNote() { await db.notes.create(); } - ``` When the bundler builds the `EmptyNote` Client Component, it will create a reference to the `createNote` function in the bundle. When the `button` is clicked, React will send a request to the server to execute the `createNote` function using the reference provided: -```js [[1, 2, "createNote"], [1, 5, "createNote"], [1, 7, "createNote"]] +```js [[1, 4, "createNote"], [1, 7, "createNote"], [1, 9, "createNote"]] +// EmptyNote.js "use client"; + import {createNote} from './actions'; function EmptyNote() { console.log(createNote); // {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNote'} - ; } ``` For more, see the docs for [`"use server"`](/reference/rsc/use-server). -### Server Functions with Actions {/*server-functions-with-actions*/} +### Calling a Server Function from an Action {/*server-functions-with-actions*/} Server Functions can be called from Actions on the client: -```js [[1, 3, "updateName"]] +```js [[1, 4, "updateName"]] +// actions.js "use server"; export async function updateName(name) { @@ -109,12 +111,15 @@ export async function updateName(name) { return {error: 'Name is required'}; } await db.users.updateName(name); + return {error: null}; } ``` -```js [[1, 3, "updateName"], [1, 13, "updateName"], [2, 11, "submitAction"], [2, 25, "submitAction"]] +```js [[1, 5, "updateName"], [1, 15, "updateName"], [2, 13, "submitAction"], [2, 28, "submitAction"]] +// UpdateName.js "use client"; +import {useState, useTransition} from 'react'; import {updateName} from './actions'; function UpdateName() { @@ -123,40 +128,60 @@ function UpdateName() { const [isPending, startTransition] = useTransition(); - const submitAction = async () => { + function submitAction() { startTransition(async () => { const {error} = await updateName(name); + // State updates after await aren't automatically marked as Transitions, + // so wrap them in another startTransition. startTransition(() => { - if (error) { - setError(error); - } else { + setError(error); + if (!error) { setName(''); } }); - }) + }); } return (
- + setName(event.target.value)} + disabled={isPending} + /> {error && Failed: {error}}
- ) + ); } ``` This allows you to access the `isPending` state of the Server Function by wrapping it in an Action on the client. -For more, see the docs for [Calling a Server Function outside of `
`](/reference/rsc/use-server#calling-a-server-function-outside-of-form) +For more, see the docs for [Calling a Server Function outside of ``](/reference/rsc/use-server#calling-a-server-function-outside-of-form). -### Server Functions with Form Actions {/*using-server-functions-with-form-actions*/} +### Passing a Server Function to the `` `action` prop {/*using-server-functions-with-form-actions*/} Server Functions work with the new Form features in React 19. -You can pass a Server Function to a Form to automatically submit the form to the server: +Pass a Server Function to the `` `action` prop to submit the form to the server. React passes the submitted [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) to the Server Function as its first argument: +```js [[1, 4, "updateName"]] +// actions.js +"use server"; + +export async function updateName(formData) { + const name = formData.get('name'); + if (typeof name !== 'string' || !name) { + throw new Error('Name is required'); + } + await db.users.updateName(name); +} +``` -```js [[1, 3, "updateName"], [1, 7, "updateName"]] +```js [[1, 4, "updateName"], [1, 8, "updateName"]] +// UpdateName.js "use client"; import {updateName} from './actions'; @@ -166,21 +191,37 @@ function UpdateName() {
- ) + ); } ``` -When the Form submission succeeds, React will automatically reset the form. You can add `useActionState` to access the pending state, last response, or to support progressive enhancement. +When the Server Function passed to the `
` `action` prop succeeds, React automatically resets the form's uncontrolled fields. Users can submit the form before its JavaScript bundle loads. Use `useActionState` to access the Action's pending state and most recent return value. For more, see the docs for [Server Functions in Forms](/reference/rsc/use-server#server-functions-in-forms). -### Server Functions with `useActionState` {/*server-functions-with-use-action-state*/} +### Calling a Server Function with `useActionState` {/*server-functions-with-use-action-state*/} + +Call a Server Function with `useActionState` to access the Action's pending state and most recent return value. The Server Function receives the previous state as its first argument and the submitted [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) as its second argument. Its return value becomes the next state: + +```js [[1, 4, "updateName"]] +// actions.js +"use server"; -You can call Server Functions with `useActionState` for the common case where you just need access to the action pending state and last returned response: +export async function updateName(previousState, formData) { + const name = formData.get('name'); + if (typeof name !== 'string' || !name) { + return {error: 'Name is required'}; + } + await db.users.updateName(name); + return {error: null}; +} +``` -```js [[1, 3, "updateName"], [1, 6, "updateName"], [2, 6, "submitAction"], [2, 9, "submitAction"]] +```js [[1, 5, "updateName"], [1, 8, "updateName"], [2, 8, "submitAction"], [2, 11, "submitAction"]] +// UpdateName.js "use client"; +import {useActionState} from 'react'; import {updateName} from './actions'; function UpdateName() { @@ -188,28 +229,30 @@ function UpdateName() { return ( - + {state.error && Failed: {state.error}}
); } ``` -When using `useActionState` with Server Functions, React will also automatically replay form submissions entered before hydration finishes. This means users can interact with your app even before the app has hydrated. +When the function passed to `useActionState` is a Server Function, users can submit the form before hydration finishes. React can display the Server Function's return value before JavaScript loads. For more, see the docs for [`useActionState`](/reference/react/useActionState). -### Progressive enhancement with `useActionState` {/*progressive-enhancement-with-useactionstate*/} +### Supporting progressive enhancement with `useActionState` {/*progressive-enhancement-with-useactionstate*/} Server Functions also support progressive enhancement with the third argument of `useActionState`. -```js [[1, 3, "updateName"], [1, 6, "updateName"], [2, 6, "/name/update"], [3, 6, "submitAction"], [3, 9, "submitAction"]] +```js [[1, 5, "updateName"], [1, 8, "updateName"], [2, 8, "/name/update"], [3, 8, "submitAction"], [3, 11, "submitAction"]] +// UpdateName.js "use client"; +import {useActionState} from 'react'; import {updateName} from './actions'; function UpdateName() { - const [, submitAction] = useActionState(updateName, null, `/name/update`); + const [, submitAction] = useActionState(updateName, {error: null}, `/name/update`); return (
@@ -219,6 +262,6 @@ function UpdateName() { } ``` -When the permalink is provided to `useActionState`, React will redirect to the provided URL if the form is submitted before the JavaScript bundle loads. +If you pass the `permalink` to `useActionState`, the browser navigates to that URL when the form is submitted before the JavaScript bundle loads. At the destination, render the same form component with the same Server Function and `permalink` so React can pass the returned state to it. For more, see the docs for [`useActionState`](/reference/react/useActionState).