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:
- Set up your project:
npm create vite@latest
npm install react@beta react-dom@beta
- 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
- 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>
);
};
- 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>
);
};
- 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>
);
};
- 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>
);
};
- 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
- 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. - Build a component that submits a contact form. Use the
useActionState
hook to handle the form submission states. - Develop a component that uploads multiple files. Use the
useActionState
hook to manage the upload process for each file. - Create a login form component that uses the
useActionState
hook to manage the login process. Display appropriate messages based on the state. - 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.