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: useFormState #55564

Merged
merged 7 commits into from Sep 22, 2023
Merged

docs: useFormState #55564

merged 7 commits into from Sep 22, 2023

Conversation

leerob
Copy link
Member

@leerob leerob commented Sep 19, 2023

This PR shows how to use a new React hook useFormState in the context of the Forms and Mutations docs. It also updates the forms example (next-forms) to show the recommended patterns for loading / error states.

Related: #55399

Success

CleanShot.2023-09-18.at.22.39.58.mp4

Loading / Error States

CleanShot.2023-09-18.at.22.41.57.mp4

Copy link

@orca-security-us orca-security-us bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orca Security Scan Summary

Status Check Issues by priority
Passed Passed Secrets high 0   medium 0   low 0   info 0 View in Orca

@leerob leerob marked this pull request as ready for review September 19, 2023 03:42
@leerob leerob requested review from a team as code owners September 19, 2023 03:42
}

export function DeleteForm({ id }: { id: number }) {
const [state, formAction] = useFormState(deleteTodo, initialState)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fun fact: You can also do deleteTodo.bind(null, id) either here or on the returned formAction. To pass the ID as an extra argument. It should preserve its ability to operate with progressive enhancement.

Gives you the same capabilities as closures but on the client. Using an actual closure on the client breaks progressive enhancement since now there's some client code running before.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which pattern would you prefer for us to recommend? I can update this if you feel .bind() is better.

Copy link
Contributor

@mayank1513 mayank1513 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

examples/next-forms/app/actions.ts Outdated Show resolved Hide resolved
examples/next-forms/app/add-form.tsx Outdated Show resolved Hide resolved
examples/next-forms/app/delete-form.tsx Outdated Show resolved Hide resolved
examples/next-forms/app/delete-form.tsx Outdated Show resolved Hide resolved
leerob and others added 2 commits September 20, 2023 19:10
@schimi-dev
Copy link

schimi-dev commented Sep 22, 2023

This PR leads to below pattern for setting error and success state being removed from the docs in favor of the new useFormState.

'use client'
 
import { create } from './actions'
import { useState } from 'react'
 
export default function Page() {
  const [message, setMessage] = useState<string>('')
 
  async function onCreate(formData: FormData) {
    const res = await create(formData)
    setMessage(res.message)
  }
 
  return (
    <form action={onCreate}>
      <input type="text" name="item" />
      <button type="submit">Add</button>
      <p>{message}</p>
    </form>
  )
}

However, unlike the pattern in the current docs, useFormState does not seem to provide a way to manipulate the state on the client side (without using a Server Action). Thus, implementing Toast/Snackbar Notifications would not work with useFormState because you can't dismiss them via client side event handlers.

With the pattern that is currently present in the docs (which would be removed by this PR), implementing Toast/Snackbar Notifications works fine.

So maybe it is better to keep this pattern in the docs as well and recommend it, if resetting state on the client side (without calling a Server Action) is desired (as it is for Toast/Snackbar Notifications).

@leerob
Copy link
Member Author

leerob commented Sep 22, 2023

@schimi-dev this approach should still work for handling toasts, did you try it out?

@schimi-dev
Copy link

schimi-dev commented Sep 22, 2023

@leerob Thanks for your quick answer. The approach in the code snippet provided above works fine. But when I was checking your commits I saw that it was replaced/removed from the examples in favor of the useFormState approach:
38ced93

However, I think it might help people if it was kept in the docs as a reference, to show that this works as well. So, maybe you could keep both approaches in the docs, the one from the code snippet and the new one describing the usage of useFormState?

@leerob
Copy link
Member Author

leerob commented Sep 22, 2023

useFormState should also work for that product requirement – this is the preferred pattern we'd like to showcase in the docs 🙏

@leerob leerob merged commit 5f4238d into canary Sep 22, 2023
56 checks passed
@leerob leerob deleted the useformstate branch September 22, 2023 21:10
@leerob
Copy link
Member Author

leerob commented Sep 22, 2023

Shipping so we can start getting more feedback – can add additional tweaks in follow up to the example if we want. Let me know.

@schimi-dev
Copy link

schimi-dev commented Sep 22, 2023

@leerob Thanks, I will play around with useFormState and check if I can build my use case with it. The interesting question for me is: "Is it possible to change/reset the state that is provided by useFromState from a source that is not that useFormState's formAction?"

Scenario:

  1. I want to open a Toast notification when the state of useFormState becomes a certain value by executing the formAction. (This part is covered by the docs and should work fine.)
  2. When the user clicks on a Cancel-Icon inside that Toast Notification I want to reset the state without executing the formAction or any other Server-side code. (This part is not covered yet.)

@bartlangelaan
Copy link
Contributor

While I really see the benefit of the useFormState API, I am glad that I had already seen the example that @schimi-dev already mentioned. It was really an eye-opener to me that the server actions can be used as 'simple functions' and that this is not something that always needs to be triggered by a form action.

I am currently working on migrating some pages to the app-structure, and I was missing this example today. Glad I found this PR so I know I didn't make this functionality up ;-)

I also think it would be great to provide a (simple) example that doesn't work with a <form> tag. For example, I am implementing a QR code scanner, which doesn't use a <form> underneath. Using server actions is great, but according to the new docs I would not know that that's possible.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants