Developer
Developer

Reputation: 3

nextjs 14 forms, which is the better pattern

I need to create a form in nextjs, in plain react some time ago I used to manage the state of each input of the form by using an useState hook, now I have seen in the official nextjs documentation that the good practice is do things like this, where user only submit the form and you validate the inputs on the function onSubmit, import React, { useState, FormEvent } from 'react'

export default function Page() {
  const [isLoading, setIsLoading] = useState<boolean>(false)
 
  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setIsLoading(true) // Set loading to true when the request starts
 
    try {
      const formData = new FormData(event.currentTarget)
      const response = await fetch('/api/submit', {
        method: 'POST',
        body: formData,
      })
 
      // Handle response if necessary
      const data = await response.json()
      // ...
    } catch (error) {
      // Handle error if necessary
      console.error(error)
    } finally {
      setIsLoading(false) // Set loading to false when the request completes
    }
  }
 
  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Loading...' : 'Submit'}
      </button>
    </form>
  )
}

Now I would like to have for example an input called phone number, I need to validate the content of it, I would like to have the forms green border while the inputed value is correct and red is incorrect, for do this i need a client component that uses useState right? I can't do it with a server component for better performance, also what about error handling, what is the best practice for handling errors on each field?

I thought to make a part of the form client component and a part of the form server component for better performance.

Upvotes: 0

Views: 1952

Answers (4)

leandronn
leandronn

Reputation: 170

When using Next.JS 14, you could take advantage of server actions to submit your form. This is a common example:

"use client";

import { useFormState, useFormStatus } from "react-dom";
import { createTodo } from "@/app/actions";

const initialState = {
  message: "",
};

function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button type="submit" aria-disabled={pending}>
      Add
    </button>
  );
}

export function AddForm() {
  const [state, formAction] = useFormState(createTodo, initialState);

  return (
    <form action={formAction}>
      <label htmlFor="todo">Enter Task</label>
      <input type="text" id="todo" name="todo" required />
      <SubmitButton />
      <p aria-live="polite" className="sr-only" role="status">
        {state?.message}
      </p>
    </form>
  );
}

and the action:

"use server"

export const createTodo(formData) => {
  // this is just one way to get the form data entries:
  const rawFormData = Object.fromEntries(formData);

  // logic of your action: here you add your logic to do with
  // the form's data
};

You can handle form validation with Zod -or any other way you could find suitable- within your server action createTodo

"use server"
import { z } from "zod";

export const createTodo(formData) => {
  const schema = z.object({
    todo: z.string().min(1),
  });

  const parse = schema.safeParse({
    todo: formData.get("todo"),
  });

  if (!parse.success) {
    return { message: "Failed to create todo" };
  }

  const data = parse.data;

  // login to handle the data
};

Next.JS recommends using required and types (e.g: type="email") for simple forms: docs

Upvotes: 0

cuginoAle
cuginoAle

Reputation: 51

I would suggest to use HTML5 and Constraint API

(Source MDN): HTML5 already has the ability to validate most user data without relying on JavaScript. This is done by using validation attributes on form elements.

required: Specifies whether a form field needs to be filled in before the form can be submitted.

minlength and maxlength: Specifies the minimum and maximum length of textual data (strings).

min and max: Specifies the minimum and maximum values of numerical input types.

type: Specifies whether the data needs to be a number, an email address, or some other specific preset type.

pattern: Specifies a regular expression that defines a pattern the entered data needs to follow.

If the data entered in a form field follows all of the rules specified by the above attributes, it is considered valid. If not, it is considered invalid.

Most browsers support the Constraint Validation API, which consists of a set of methods and properties that enable checking values that users have entered into form controls, before submitting the values to the server.

With these tools at your disposal you can build custom components for easy and efficient form management.

Live example: https://codesandbox.io/p/sandbox/simple-form-validation-ghxg1m?file=%2Fsrc%2FApp.tsx

Upvotes: 0

Sandeep M
Sandeep M

Reputation: 350

Definitely if you want to handle the errors on onChange event, then you have to use the useState hook, which makes the components client side. Other then that, there are libraries like React Hook Form or Formik that could help with validation on value change. They provide inbuilt error handling mechanisms to handle errors for each field, with the functionality to have custom error messages.

However, there is another method if you want the values to be validated on the click of button. We can acheive that using useRef. Here you can create multiple refs for the form elements and attach it to each input field. Later on click of submit, you can get the values using ref.current.value. The biggest usecase of this is, you can prevent rerendering of component on each keystroke. The values will be stored in the variable and then can be validated on the Submit button.

React hook form

Formik

Upvotes: -1

Commodore64
Commodore64

Reputation: 102

Your example is definitely a better pattern I would say. You can also checkout libraries like react-hook-form and zod for validation. It can help saving a lot of time, although you might want to watch out for compatibility with SSR

Upvotes: 0

Related Questions