useFormStatus hook in react

useFormStatus hook in react

At the time of the writing this postuseFormStatus*hook in react is currently only available in React’s Canary and experimental channels. *

useFormStatus*is part of reacts effort for server component. useFormStatus is a react hook hence the prefix "use" that captures details on the most recent form submitted.

example: useFormStatus
const { pending, data, method, action } = useFormStatus();

useFormStatus retuns an obj with 4 valuespendingdatamethodaction

pending*tells you if the current form is doing an async task and if the form is actively submitting an action

example: using tailwind and react
import { useFormStatus } from "react-dom";
import { type CSSProperties } from "react";
import LoadingSpinner from "@/components/loadingSpinner";

function SubmitButton({
  copy = "submit",
  className = "",
  style = {},
}: {
  style?: CSSProperties;
  copy?: string;
  className?: string;
}) {
  const { pending } = useFormStatus();

  const customClasses = Object.keys(style).length
    ? "hover:!bg-[#2d2d32] hover:!text-[#ffffff]"
    : "hover:bg-[#f0db4e] hover:text-[#2d2d32]";

  return (
    <button
      type="submit"
      disabled={pending}
      style={{ ...style }}
      className={`rounded w-full font-mono bg-[#2d2d32] text-[#ffffff] p-2 mt-1 font-medium transition ${customClasses} ${className} `}
    >
      {pending ? (
        <span className="w-full flex items-center justify-center">
          <LoadingSpinner />
        </span>
      ) : (
        copy
      )}
    </button>
  )
}
import { serverAction } from "@/app/actions";
import SubmitButton from '@component/submitButton'
function myForm(){

  return (
    <form action={serverAction}>
      <div className="flex flex-col font-mono">
              <div>
                <label className="flex flex-col mb-3 font-mono">
                  <span className="mb-1 text-xs font-medium">First Name:</span>
                  <input
                    type="text"
                    name="firstName"
                    placeholder="Tony"
                    required
                    className="border p-2 focus:shadow-md focus:border-[#f0db4e] outline-0 rounded placeholder:text-small"
                  />
                </label>

                <label className="flex flex-col mb-3 font-mono">
                  <span className="mb-1 text-xs font-medium">Last Name</span>
                  <input
                    type="text"
                    placeholder="Stark"
                    name="lastName"
                    required
                    className="border p-2 focus:shadow-md focus:border-[#f0db4e] outline-0 rounded placeholder:text-small"
                  />
                </label>

                <label className="flex flex-col mb-3 font-mono">
                  <span className="mb-1 text-xs font-medium">Email</span>
                  <input
                    type="email"
                    placeholder="tonystark@starkindustries.com"
                    name="email"
                    required
                    className="border p-2 focus:shadow-md focus:border-[#f0db4e] outline-0 rounded placeholder:text-small"
                  />
                </label>

                <legend className="mb-1 text-[#2d2d32] text-xs font-medium">
                  <span> Prior Experiance</span>
                </legend>

                <fieldset className="pr-2 py-2 flex flex-col md:flex-row align-center flex-wrap justify-between">
                  <div className="mb-3 md:mb-2 text-[#2d2d32] hover:font-bold transition-all font-medium text-sm">
                    <label
                      htmlFor={`${
                        props.idFor ? props.idFor + "beginner" : "beginner"
                      }`}
                      className="w-full cursor-pointer items-center flex"
                    >
                      <input
                        type="radio"
                        id={`${
                          props.idFor ? props.idFor + "beginner" : "beginner"
                        }`}
                        name="priorExperiance"
                        value="beginner"
                        required
                      />
                      <span className="pl-2">Beginner</span>
                    </label>
                  </div>

                  <div className="mb-3 md:mb-0 md:mx-4 md:mb-2 lg:mx-6 text-[#2d2d32] hover:font-bold transition-all font-medium text-sm">
                    <label
                      htmlFor={`${
                        props.idFor
                          ? props.idFor + "intermediate"
                          : "intermediate"
                      }`}
                      className="w-full cursor-pointer items-center flex"
                    >
                      <input
                        type="radio"
                        id={`${
                          props.idFor
                            ? props.idFor + "intermediate"
                            : "intermediate"
                        }`}
                        name="priorExperiance"
                        value="intermediate"
                        className="checked:bg-[#f0db4e]"
                      />
                      <span className="pl-2">Intermediate</span>
                    </label>
                  </div>

                  <div className="md:mb-2 text-[#2d2d32] font-medium hover:font-bold transition-all text-sm">
                    <label
                      htmlFor={`${
                        props.idFor ? props.idFor + "advanced" : "advanced"
                      }`}
                      className="w-full cursor-pointer items-center flex"
                    >
                      <input
                        type="radio"
                        id={`${
                          props.idFor ? props.idFor + "advanced" : "advanced"
                        }`}
                        name="priorExperiance"
                        value="advanced"
                      />
                      <span className="pl-2">Advanced</span>
                    </label>
                  </div>
                </fieldset>
              </div>

              <div className="mt-1">
                <SubmitButton {...submitButton} />
              </div>
            </div>
    </form>
  )
}

the code above results in the the following ui

use-form-status-form

then when we submit the form it will trigger the server action. and while the asynchronous task is running the state of pending will be true and react will render the loadingSpinner component

use-form-status-form-loading
const { data} = useFormStatus();

data*property that gets retured from the useFormStatus hook has information about the data being submitted.

for the example we demonstated above the

const { data } = useFormStatus();

console.log(data) //  will result in the formdata being submitted

so data now has access to firstName, lastName , email ...etc fields that we are capturing data for.

const { meathod} = useFormStatus();

console.log(meathod) // GET (unless otherwise specified)

meathod*has information about the HTTP methods (GET, POST, PUT, PATCH, DELETE) about the type of request being submitted. by default the request is a GET request

const { action} = useFormStatus();

action*is the refrence to the server action function passes to the form.

for the code example used above the action is

const { action} = useFormStatus();

<form action={serverAction}>
// some fileds 

<SubmitButton {...submitButton} />
</form>

console.log(action) // is refrence to `serverAction`  action prop of the form

some things we need to be aware of when using this hook is

  • The useFormStatus Hook must be called from a component that is rendered inside a form.
  • useFormStatus will only return status information for a parent form. It will not return status information for any form rendered in that same component or children components.