useActionState react hook

The useActionState hook is introduced in React 19. This hook simplifies managing asynchronous operations, especially in forms, by handling states like “idle”, “loading”, “success”, and “error”.

Simple Explanation

The useActionState hook helps you manage the state of an action, such as a form submission or an API call, in a more streamlined way. It reduces the need for multiple state variables and boilerplate code, making your code cleaner and easier to maintain.

Example Implementation

Here’s how you can use the useActionState hook:

  1. Set up your project:
   npm create vite@latest
   npm install react@beta react-dom@beta
  1. Create a component using useActionState:
   import { useActionState } from 'react';

   const LoginForm = () => {
     const [state, submitAction, isPending] = useActionState(async (formData) => {
       const response = await fakeApiCall(formData.get('username'), formData.get('password'));
       return { data: response.data, error: null };
     }, { data: null, error: null });

     return (
       <form onSubmit={submitAction}>
         <input name="username" placeholder="Username" required />
         <input name="password" type="password" placeholder="Password" required />
         <button type="submit" disabled={isPending}>Login</button>
         {state.error && <p>{state.error}</p>}
         {state.data && <p>Welcome, {state.data.username}!</p>}
       </form>
     );
   };

Examples

  1. Fetching Data from an API:
   import { useActionState } from 'react';

   const FetchDataComponent = () => {
     const [state, fetchData, isPending] = useActionState(async () => {
       const response = await fetch('https://api.example.com/data');
       if (!response.ok) throw new Error('Network response was not ok');
       return { data: await response.json(), error: null };
     }, { data: null, error: null });

     return (
       <div>
         <button onClick={fetchData} disabled={isPending}>Fetch Data</button>
         {isPending && <p>Loading...</p>}
         {state.data && <p>Data: {JSON.stringify(state.data)}</p>}
         {state.error && <p>Error: {state.error}</p>}
       </div>
     );
   };
  1. Form Submission:
   import { useActionState } from 'react';

   const FormComponent = () => {
     const [state, submitForm, isPending] = useActionState(async (formData) => {
       // Simulate form submission
       await new Promise((resolve) => setTimeout(resolve, 1000));
       return { data: 'Form submitted successfully!', error: null };
     }, { data: null, error: null });

     return (
       <form onSubmit={submitForm}>
         <button type="submit" disabled={isPending}>Submit</button>
         {isPending && <p>Submitting...</p>}
         {state.data && <p>{state.data}</p>}
         {state.error && <p>Error: {state.error}</p>}
       </form>
     );
   };
  1. File Upload:
   import { useActionState } from 'react';

   const FileUploadComponent = () => {
     const [state, uploadFile, isPending] = useActionState(async (formData) => {
       // Simulate file upload
       await new Promise((resolve) => setTimeout(resolve, 2000));
       return { data: 'File uploaded successfully!', error: null };
     }, { data: null, error: null });

     return (
       <div>
         <input type="file" onChange={(e) => uploadFile(e.target.files[0])} />
         {isPending && <p>Uploading...</p>}
         {state.data && <p>{state.data}</p>}
         {state.error && <p>Error: {state.error}</p>}
       </div>
     );
   };
  1. Login Process:
   import { useActionState } from 'react';

   const LoginComponent = () => {
     const [state, login, isPending] = useActionState(async (formData) => {
       // Simulate login process
       await new Promise((resolve) => setTimeout(resolve, 1500));
       return { data: 'Login successful!', error: null };
     }, { data: null, error: null });

     return (
       <div>
         <button onClick={login} disabled={isPending}>Login</button>
         {isPending && <p>Logging in...</p>}
         {state.data && <p>{state.data}</p>}
         {state.error && <p>Error: {state.error}</p>}
       </div>
     );
   };
  1. Data Deletion:
   import { useActionState } from 'react';

   const DeleteDataComponent = () => {
     const [state, deleteData, isPending] = useActionState(async () => {
       // Simulate data deletion
       await new Promise((resolve) => setTimeout(resolve, 1000));
       return { data: 'Data deleted successfully!', error: null };
     }, { data: null, error: null });

     return (
       <div>
         <button onClick={deleteData} disabled={isPending}>Delete Data</button>
         {isPending && <p>Deleting...</p>}
         {state.data && <p>{state.data}</p>}
         {state.error && <p>Error: {state.error}</p>}
       </div>
     );
   };

Practice Exercises

  1. Create a component that fetches user data from an API and displays it. Use the useActionState hook to manage the loading, success, and error states.
  2. Build a component that submits a contact form. Use the useActionState hook to handle the form submission states.
  3. Develop a component that uploads multiple files. Use the useActionState hook to manage the upload process for each file.
  4. Create a login form component that uses the useActionState hook to manage the login process. Display appropriate messages based on the state.
  5. Implement a component that deletes items from a list. Use the useActionState hook to manage the deletion process and update the UI accordingly.

These exercises will help you get comfortable with using the useActionState hook in various scenarios.

Published
Categorized as React