DEV Community

Cover image for React 19 Hooks Explained: Everything You Need to Know
Vishnu Satheesh
Vishnu Satheesh

Posted on

React 19 Hooks Explained: Everything You Need to Know

React 19 comes with some awesome new hooks that make handling common tasks in React development way easier. These hooks focus on things like state management, form handling, and working with async updates.
Here’s a quick look at the new hooks in this update:

Hook Name Description
useFormState() Manages form data and state efficiently.
useFormStatus() Tracks the status of form submission.
useActionState() Simplifies managing async actions.
useOptimistic() Handles optimistic UI updates.
use() Allows awaiting promises inside components.

In this guide, we’ll dive into how we used to handle these processes before these hooks came along, and we’ll see how these new hooks make everything smoother and more efficient. Let’s get into it!

useFormState()

Before React 19, to manage form data, you would typically use a useState for each form field or create one state object for all fields. This meant writing a custom onChange for each field to manually update the state, which could get complicated, especially with larger forms.

Now, with the new useFormState() hook in React 19, managing form data is much easier. It tracks all form inputs in one place and automatically updates the state when any field changes.

This means you don’t need separate state handlers for each input field anymore. Everything stays in sync and is much cleaner, especially for more complex forms.

For example: Older way of handling form field states

import { useState } from 'react';

function ProfileForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    await fetch('/api/profile', { method: 'POST',
    body: JSON.stringify({name, email}) }); 
  };

return (
  <form onSubmit={handleSubmit}>
    <input 
      name="name"
      value={name}
      onChange={(e) => setName(e.target.value)}
      placeholder="Name"
     />
    <input 
      name="email"
      value={email}
      onChange={(e) => setEmail(e.target.value)}
      placeholder="Email"
     />
    <button type="submit">Submit</button>
 </form>
)

}
Enter fullscreen mode Exit fullscreen mode

For example: With useFormState() hook

import { useFormState } from 'react';

function ProfileForm() { 
  const [formData, setFormData] = useFormState({
   name: '',
   email: ''
  });

 const handleChange = (e) => {
   setFormData({ ...formData, [e.target.name]: e.target.value });
 }

 const handleSubmit = () => {
   // Handle form submission with the entire formData object
 }

 return (
   <form onSubmit={handleSubmit}>
      <input name="name" value={formData.name} onChange={handleChange} />
       <input name="email" value={formData.email} onChange={handleChange} />
       <button type="submit">Submit</button>
    </form>
 );
}
Enter fullscreen mode Exit fullscreen mode

useFormStatus()

Before React 19, developers had to use multiple state variables (like loading, error, success, submitting, etc.) to track and manage the status of a form. This made the code more complex, especially when trying to disable the submit button or show a loading indicator.

In React 19, the useFormStatus() hook simplifies this process. It helps you easily track the form's status (whether it's submitting, submitted, idle, or has an error). This makes it much easier to manage things like showing a loading spinner or disabling the submit button.

For example: Older way of handling form submission

import { useState } from 'react';

function ProfileForm() {
  const [name, setName] = useState('');
  const [pending, setPending] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setPending(true);
    await fetch('/api/profile', { method: 'POST',
    body: JSON.stringify({name, email}) }); 
    setPending(false);
    setName("");
  };

return (
  <form onSubmit={handleSubmit}>
    <input 
      type="text"
      name="name"
      value={name}
      onChange={(e) => setName(e.target.value)}
      placeholder="Enter Name"
     />
    <button type="submit">Submit</button>
    {pending && <p> Submitting {name} ... </p>}
 </form>
)

}
Enter fullscreen mode Exit fullscreen mode

Example: Using the useFormStatus Hook

import { useFormStatus } from ' react ' ;

function LoginForm() {
   const { pending, data, method, action } = useFormStatus();

   const handleSubmit = async ( event ) => {
      event.preventDefault();
      // Simulate form submission with fetch  or other method
};

   return (
      <form method="POST" action="/login" onSubmit={ handleSubmit }>
         <h3>Login</h3>
         <label>
            Username:
            <input name="username" type="text" required/>
         </label>
         <label>
            Password:
            <input name="password" type="password" required/>
         </label>
         <button type="submit" disabled={pending}>
            { pending ? 'Logging in....'  : 'Login' }
         </button>

         { /* Display success message if data is available */ }
         {data && <p>Welcome back, {data.username}!</p>}

         { /* Display form metadata for debugging purposes */ }
         <p>Form Method: {method}</p>
         <p>Form Action: {action}</p>
      </form>
   );
}
Enter fullscreen mode Exit fullscreen mode

The useFormStatus hook helps you manage a form's state in a user-friendly way. It provides useful properties like pending, data, method, and action, allowing you to improve the user experience with loading indicators, success messages, and error handling.

Here's what each property does:

  • pending: A boolean that is true while the form is submitting and false once it's done.
  • data: Stores any response data from the form submission.
  • method: Shows the HTTP method used (e.g., POST or GET).
  • action: The URL or endpoint where the form is submitted.

useActionState()

In earlier versions of React, handling asynchronous action states often required using multiple useState variables to track the status of each operation:

import { useState } from ' react ';

function SubmitButton() {
   const [ isSubmitting, setIsSubmitting ] = useState(false);
   const [ error, setError ] = useState(null);
   const [ success, setSuccess ] = useState(false);

   const handleSubmit = async() => {
   setIsSubmitting(true);
   setError(null);
   setSuccess(false);
   try {
      await fetch('/api/submit' , { method: 'POST' });
      setSuccess(true);
   } catch(err) {
      setError('Submission failed');
   }   
   }  finally {
      setIsSubmitting(false);
   }   
};

   return (
      <div>
         <button onClick= {handleSubmit} disabled= {isSubmitting }>
            { isSubmitting ? 'Submitting ... ' : 'Submit' }
         </button>
         { error && <p>{ error }</p> }
         { success && <p>Success!</p> }
      </div>
   );
}

Enter fullscreen mode Exit fullscreen mode

The useActionState is a generalized hook designed to manage the lifecycle of these actions or tasks in a more streamlined way, by providing a standardized way to manage and respond to each stage of the operations.

It helps maintain a clear, controlled flow in UI updates, especially when actions need to track multiple statuses like "idle," "loading," "success," or
"error."

Using our example above, the useActionState hook will help simplify this pattern by encapsulating the various states of an action into a single hook, allowing you to handle the loading, success, and error states more concisely.

Example: Using the useActionState Hook

import { useActionState } from 'react'

function SubmitButton() {

   const [submitStatus, performSubmit] = useActionState(async () => {
    await fetch('/api/submit', { method: 'POST' });
});

   return (
    <div>
      <button onClick={performSubmit} disabled={submitStatus.isLoading}>
    {submitStatus.isLoading ? 'Submitting ...' : 'Submit'}
      </button>
    {submitStatus.error && <p>{submitStatus.error.message}</p>}
    {submitStatus.isSuccess && <p>Success!</p>}
   </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

With useActionState:

  • submitStatus is an object that contains properties like isLoading, isSuccess, and error, which you can use to conditionally render UI based on the action's current state.
  • performSubmit is the function to invoke the action, which automatically updates submitStatus.
  • There's no need for additional state variables or manual error handling in the UI logic, reducing code complexity and making it more readable.

useOptimistic()

Before React 19, to create optimistic UI updates, where changes appear instantly before server confirmation, we often used a mix of state variables and asynchronous functions.

For instance, we'd set a state variable to indicate an immediate UI update, then send the API request and update the state again based on the response.
This approach was functional but required a lot of boilerplate code and error handling to ensure consistency between UI and server state.

In React 19, the useOptimistic hook simplifies this by providing an easy way to handle optimistic updates.
This hook allows you to update the UI immediately, even as the actual server confirmation is pending.
If the server request fails, useOptimistic can be configured to revert the state, thus providing a smooth experience with minimal code.

For example: Older way of handling optimistic update

import { useState } from 'react'

function LikeButton({ postId }) {
  const [isLiked, setIsLiked] = useState(false);
  const [error, setError] = useState(null);

  const handleLike = async () => {
   // Update state immediately
    setIsLiked(!isLiked);

   try{
    // Make the API call
     await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
   } catch (err) {
   // Revert state if an error occurs
    setIsLiked(isLiked);
    setError('Failed to update like status');
   }
 };

 return (
  <div>
    <button onClick={handleLike}>
      {isLiked ? 'Unlike' : 'Like'}
    </button>
      {error && <p>{error}</p>}
  </div>
 );
}

Enter fullscreen mode Exit fullscreen mode

With the useOptimistic hook, React 19 simplifies this pattern by automatically managing an optimistic state. It allows you to update the UI instantly without needing extra error-handling code, making your components cleaner and more efficient.

Use()

In earlier versions of React, handling asynchronous tasks like data fetching within components typically required useEffect and useState. This often resulted in complex, nested logic, especially when managing loading and error states.

With React 19, the new use hook simplifies this process by allowing you to directly await a promise inside components. This eliminates the need for extra useEffect and useState code, making data fetching feel more like synchronous code. The result? Cleaner, more readable, and maintainable components.

Example: asynchronous data fetching using useEffect and useState hooks.

import { useState, useEffect } from 'react';

function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch('/api/user');
        const data = await response.json();
        setUser(data);
       } finally {
         setLoading(false);
       }
      };
       fetchUser();
     },[]);

 if (loading) return <p>Loading ... </p>;
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div> 
  );
}
Enter fullscreen mode Exit fullscreen mode

Example: Using the new use() hook for asynchronous fetching

import { use } from 'react'

async function fetchUser() {
  const response = await fetch('/api/user');
  return response.json();
}

function UserProfile() {
  const user = use(fetchUser());

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

React 19 introduces a powerful set of hooks that make state management, form handling, and asynchronous operations easier than ever. With hooks like useFormState, useFormStatus, useActionState, useOptimistic, and use, React now provides more intuitive and efficient ways to handle complex UI interactions with less boilerplate code.

By leveraging these new hooks, you can write cleaner, more readable code while improving the user experience in your applications. Keep exploring these features to stay ahead in modern React development!

Happy coding! 🚀

💡 Let’s Connect!
📩 Email me at: getintouchwithvishnu@gmail.com
☕ Support my work: Buy me a Coffee

Top comments (1)

Collapse
 
ravi-coding profile image
Ravindra Kumar

Good !