-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix Server Functions documentation examples #8538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
smith558
wants to merge
5
commits into
reactjs:main
Choose a base branch
from
smith558:patch-13
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+65
−34
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
47539b0
Fix Server Functions documentation examples
smith558 492e403
Fix code snippet highlights
smith558 08dceff
Clarify wording
smith558 623256a
Update section titles for clarity
smith558 06a3347
Polish Server Functions examples: consistent headings, reset wording,…
aurorascharff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -34,23 +34,23 @@ 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: | ||||||
|
|
||||||
| ```js [[2, 7, "'use server'"], [1, 5, "createNoteAction"], [1, 12, "createNoteAction"]] | ||||||
| // Server Component | ||||||
| import Button from './Button'; | ||||||
|
|
||||||
| function EmptyNote () { | ||||||
| function EmptyNote() { | ||||||
| async function createNoteAction() { | ||||||
| // Server Function | ||||||
| 'use server'; | ||||||
|
|
||||||
| await db.notes.create(); | ||||||
| } | ||||||
|
|
||||||
| return <Button onClick={createNoteAction}/>; | ||||||
| return <Button onClick={createNoteAction} />; | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
|
|
@@ -62,14 +62,13 @@ When React renders the `EmptyNote` Server Component, it will create a reference | |||||
| export default function Button({onClick}) { | ||||||
| console.log(onClick); | ||||||
| // {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNoteAction'} | ||||||
| return <button onClick={() => onClick()}>Create Empty Note</button> | ||||||
| return <button onClick={() => onClick()}>Create Empty Note</button>; | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| 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: | ||||||
|
|
||||||
|
|
@@ -79,25 +78,23 @@ Client Components can import Server Functions from files that use the `"use serv | |||||
| 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, 3, "createNote"], [1, 6, "createNote"]] | ||||||
| "use client"; | ||||||
|
|
||||||
| import {createNote} from './actions'; | ||||||
|
|
||||||
| function EmptyNote() { | ||||||
| console.log(createNote); | ||||||
| // {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNote'} | ||||||
| <button onClick={() => createNote()} /> | ||||||
| return <button onClick={() => createNote()}>Create Empty Note</button>; | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| 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: | ||||||
|
|
||||||
|
|
@@ -109,12 +106,14 @@ 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, 4, "updateName"], [1, 14, "updateName"], [2, 12, "submitAction"], [2, 26, "submitAction"]] | ||||||
| "use client"; | ||||||
|
|
||||||
| import {useState, useTransition} from 'react'; | ||||||
| import {updateName} from './actions'; | ||||||
|
|
||||||
| function UpdateName() { | ||||||
|
|
@@ -123,38 +122,55 @@ function UpdateName() { | |||||
|
|
||||||
| const [isPending, startTransition] = useTransition(); | ||||||
|
|
||||||
| const submitAction = async () => { | ||||||
| function submitAction() { | ||||||
| startTransition(async () => { | ||||||
| const {error} = await updateName(name); | ||||||
| // State updates after an await aren't automatically Transitions, so wrap them | ||||||
| startTransition(() => { | ||||||
| if (error) { | ||||||
| setError(error); | ||||||
| } else { | ||||||
| setError(error); | ||||||
| if (!error) { | ||||||
| setName(''); | ||||||
| } | ||||||
| }); | ||||||
| }) | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| return ( | ||||||
| <form action={submitAction}> | ||||||
| <input type="text" name="name" disabled={isPending}/> | ||||||
| <input | ||||||
| type="text" | ||||||
| name="name" | ||||||
| value={name} | ||||||
| onChange={event => setName(event.target.value)} | ||||||
| disabled={isPending} | ||||||
| /> | ||||||
| {error && <span>Failed: {error}</span>} | ||||||
| </form> | ||||||
| ) | ||||||
| ); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| 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 `<form>`](/reference/rsc/use-server#calling-a-server-function-outside-of-form) | ||||||
| For more, see the docs for [Calling a Server Function outside of `<form>`](/reference/rsc/use-server#calling-a-server-function-outside-of-form). | ||||||
|
|
||||||
| ### Server Functions with Form Actions {/*using-server-functions-with-form-actions*/} | ||||||
| ### Using a Server Function as a Form Action {/*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: | ||||||
| You can pass a Server Function to a Form to automatically submit the form to the server. React passes the submitted `FormData` to the Server Function as its first argument: | ||||||
|
|
||||||
| ```js [[1, 3, "updateName"]] | ||||||
| "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"]] | ||||||
| "use client"; | ||||||
|
|
@@ -166,50 +182,65 @@ function UpdateName() { | |||||
| <form action={updateName}> | ||||||
| <input type="text" name="name" /> | ||||||
| </form> | ||||||
| ) | ||||||
| ); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| 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 Form Action succeeds, React will automatically reset the form's uncontrolled fields. Server Function forms can be submitted before the JavaScript bundle loads. You can add `useActionState` to access the pending state and last response. | ||||||
|
|
||||||
| 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*/} | ||||||
|
|
||||||
| 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. The Server Function receives the previous state as its first argument and the submitted `FormData` as its second argument. Its return value becomes the next state: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| ```js [[1, 3, "updateName"]] | ||||||
| "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, 4, "updateName"], [1, 7, "updateName"], [2, 7, "submitAction"], [2, 10, "submitAction"]] | ||||||
| "use client"; | ||||||
|
|
||||||
| import {useActionState} from 'react'; | ||||||
| import {updateName} from './actions'; | ||||||
|
|
||||||
| function UpdateName() { | ||||||
| const [state, submitAction, isPending] = useActionState(updateName, {error: null}); | ||||||
|
|
||||||
| return ( | ||||||
| <form action={submitAction}> | ||||||
| <input type="text" name="name" disabled={isPending}/> | ||||||
| <input type="text" name="name" disabled={isPending} /> | ||||||
| {state.error && <span>Failed: {state.error}</span>} | ||||||
| </form> | ||||||
| ); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| 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 using `useActionState` with a Server Function, the form can be submitted before hydration finishes and the Server Function's response can be shown before the app has hydrated. | ||||||
|
|
||||||
| 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, 4, "updateName"], [1, 7, "updateName"], [2, 7, "/name/update"], [3, 7, "submitAction"], [3, 10, "submitAction"]] | ||||||
| "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 ( | ||||||
| <form action={submitAction}> | ||||||
|
|
@@ -219,6 +250,6 @@ function UpdateName() { | |||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| When the <CodeStep step={2}>permalink</CodeStep> is provided to `useActionState`, React will redirect to the provided URL if the form is submitted before the JavaScript bundle loads. | ||||||
| When the <CodeStep step={2}>permalink</CodeStep> is provided to `useActionState`, the browser will navigate to the provided URL if the form is submitted before the JavaScript bundle loads. At the destination, render `useActionState` with the same Server Function and permalink so React can pass the returned state to it. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ I really like pointing out that this is browser behavior, not React behavior. |
||||||
|
|
||||||
| For more, see the docs for [`useActionState`](/reference/react/useActionState). | ||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should add a link to MDN form data here so that users can see what is included in the form data
FormData link so users know what functions are available.