Skip to content
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

Docs: Server Actions - clarify "... compose additional behaviour with Client Actions" and add an Example for it #53929

Closed
schimi-dev opened this issue Aug 11, 2023 · 3 comments
Assignees
Labels
Documentation Related to Next.js' official documentation. locked

Comments

@schimi-dev
Copy link

What is the improvement or update you wish to see?

Currently, in the docs regarding Server Actions it is stated: "Although Server Actions have the additional benefit of not relying on client JavaScript, you can still compose additional behavior with Client Actions where desired without sacrificing interactivity."

It would be good to clarify what is meant by "... compose additional behaviour with Client Actions". Does this mean I can call a Server Action inside a Client Action and pass that Client Action as action prop to a form. I would interpret this as follows:

Assume there is a file named actions.ts with the 'use server' directive on top. In such a File the following Server Action is declared:

'use server'

function updateItem(id: string, settings: TSettings) {
    // apply the new Settings to the item with that id
    revalidatePath('/');
}

Is it possible to wrap this Server Action with a Client Action and then pass that Client Action to a form's action prop inside a Client Component as follows:

'use client'

import { updateItem } from './actions';

export default function UpdateItemForm({ item }: { item: TItem }) {

    const clientAction = async (formData: FormData) => {
        const name = formData.get('name')
        const description = formData.get('description')
        await updateItem(item.id, { name, description })
    }

    return (
        <form action={clientAction}>
            <input type="text" name="name" defaultValue={item.name} />
            <input type="text" name="description" defaultValue={item.description} />
        </form>

    )
}

Could you clarify what is meant by "... compose additional behaviour with Client Actions" and maybe add this or a similar example to the docs if what I described is a legal scenario. From what I tested it works fine to compose Server Actions and Client Actions that way, with the only limitation that this does not work with JS disabled in the Browser.

Is there any context that might help us understand?

Preprocessing FormData directly inside a Client Action in a Client Component and then calling a Server Action with the already preprocessed data might be quite a common use case. As described in the example, you could easily pass additional info that might not be reflected in the FormData (e.g. item.id) to the Server Action. Moreover, a use case for this might be complex inputs like Autocompletes or DatePickers, where the corresponding values might not be correctly reflected in the FormData object.

So if the way of composing Server and Client Actions I described above is allowed, this should probably be stated in the docs. If this is not allowd then an explanation why this is not possible and what is meant by "... compose additional behaviour with Client Actions" would be nice.

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#progressive-enhancement

@schimi-dev schimi-dev added the Documentation Related to Next.js' official documentation. label Aug 11, 2023
@schimi-dev schimi-dev changed the title Docs: Clarify *"... compose additional behaviour with Client Actions"* and add an Example for it Docs: Clarify "... compose additional behaviour with Client Actions" and add an Example for it Aug 11, 2023
@schimi-dev schimi-dev changed the title Docs: Clarify "... compose additional behaviour with Client Actions" and add an Example for it Docs: Server Actions - clarify "... compose additional behaviour with Client Actions" and add an Example for it Aug 11, 2023
@shuding shuding self-assigned this Aug 14, 2023
@shuding
Copy link
Member

shuding commented Aug 14, 2023

We'll make the documentation more clear, but I'll also write a quick answer here. In the example below, you're using a normal async function clientAction as the action prop. It is not a Server Action because it isn't annotated with "use server":

'use client'

import { updateItem } from './actions';

export default function UpdateItemForm({ item }: { item: TItem }) {

    const clientAction = async (formData: FormData) => {
        const name = formData.get('name')
        const description = formData.get('description')
        await updateItem(item.id, { name, description })
    }

    return (
        <form action={clientAction}>
            <input type="text" name="name" defaultValue={item.name} />
            <input type="text" name="description" defaultValue={item.description} />
        </form>

    )
}

However, it is not allowed to define inlined "use server" function inside Client Components. To reference to variables from the closure (e.g. item.id in your example), you can use .bind:

'use client'

import { updateItem } from './actions';

export default function UpdateItemForm({ item }: { item: TItem }) {
    return (
        <form action={updateItem.bind(null, item.id)}>
            <input type="text" name="name" defaultValue={item.name} />
            <input type="text" name="description" defaultValue={item.description} />
        </form>

    )
}

And then change your Server Action's arguments to accept both id and formData:

'use server'

function updateItem(id: string, formData: FormData) {
    const name = formData.get('name')
    const description = formData.get('description')
    const settings = { name, description }

    ...
}

@schimi-dev
Copy link
Author

@shuding Thanks, your answer together with the Forms and mutations docs. PR answers my question very well. Thanks for being that responsive, this is really appreciated!

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Documentation Related to Next.js' official documentation. locked
Projects
None yet
Development

No branches or pull requests

2 participants