Hook Signature
/**
* @param {string} initialStatus - Optional initial status for the action. Defaults to "idle".
* @returns {object} The action state API.
*/
function useAsyncState(initialStatus: Status = 'idle'): ActionStateAPI
| Parameter | Type | Default | Description |
|---|---|---|---|
initialStatus | string | "idle" | The starting state of the hook: "idle", "pending", "success", or "error". |
Return API (ActionStateAPI)
The hook returns an object containing state variables, action dispatchers, and declarative rendering components.
State Properties
| Property | Type | Description |
|---|---|---|
data | any | The payload returned by the successful asynchronous callback. Null otherwise. |
error | any | The error payload (typically a string message) captured during a failed action. Null otherwise. |
status | string | The current state: "idle", "pending", "success", or "error". |
isPending | boolean | true if the action is currently executing. |
isError | boolean | true if the action completed with an error. |
isSuccess | boolean | true if the action completed successfully. |
Action Functions
| Function | Signature | Description |
|---|---|---|
startAction | (callback: () => Promise<T>) => Promise<T> | Executes the provided asynchronous function. Updates status to "pending" before execution and to "success" or "error" after completion. |
reset | () => void | Resets the state (data and error) and sets status back to "idle". |
Conditional Rendering Components
The hook provides a Unified Renderer for a low-code approach, along with Granular Renders for fine-grained control.
| Component | Usage Pattern | Description |
|---|---|---|
Renderer | Unified, Low-Code | A single component that accepts idle, pending, success, and error props to map the entire state flow declaratively. |
RenderPending | Granular | Renders children only when isPending is true. |
RenderSuccess | Granular | Renders children only on isSuccess. Supports a function-as-child pattern to pass data. |
RenderError | Granular | Renders children only on isError. Supports a function-as-child pattern to pass error. |
Code Usage
This section demonstrates a React component that fetches user data from JSONPlaceholder.
1. Example Without useAsyncState
This approach requires manual management of three separate state variables and explicit conditional logic in the JSX.
import React, { useState, useCallback } from 'react';
const fetchUser = async (id) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
return response.json();
};
function TraditionalFetcher({ userId = 1 }) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const startFetch = useCallback(async () => {
setIsPending(true);
setError(null);
setData(null);
try {
const user = await fetchUser(userId);
setData(user);
} catch (err) {
setError(err.message);
} finally {
setIsPending(false);
}
}, [userId]);
const reset = () => {
setData(null);
setError(null);
setIsPending(false);
};
return (
<div>
<h3>1. Traditional Fetcher (High-Code)</h3>
<button onClick={startFetch} disabled={isPending}>
{isPending ? 'Fetching...' : 'Fetch User'}
</button>
<button onClick={reset}>Reset</button>
{/* Manual Conditional Logic */}
{isPending && <p>Loading user data...</p>}
{error && <p>Error: {error}</p>}
{data && (
<div>
<h4>User: {data.name}</h4>
<p>Email: {data.email}</p>
</div>
)}
</div>
);
}
2. Example With useAsyncState
This approach uses the unified Renderer for a declarative, low-code component structure. All state management and conditional rendering logic are delegated to the hook.
import React from 'react';
// import { useAsyncState } from './useAsyncState'; // Assume hook import
// NOTE: useAsyncState and fetchUser are assumed to be available.
function HookFetcher({ userId = 1 }) {
// 1. Destructure the action functions and the unified renderer
const {
startAction,
reset,
Renderer,
status
} = useAsyncState();
const handleFetch = () => startAction(() => fetchUser(userId));
return (
<div>
<h3>2. Hook Fetcher (Low-Code)</h3>
<button onClick={handleFetch} disabled={status === 'pending'}>
Fetch User
</button>
<button onClick={reset}>Reset</button>
{/* Declarative, Low-Code Rendering */}
<Renderer
idle={<p>Click to fetch user data.</p>}
pending={
<p>Loading user data...</p>
}
success={(user) => (
<div>
<h4>User: {user.name}</h4>
<p>Email: {user.email}</p>
</div>
)}
error={(errorMsg) => (
<p>Error: {errorMsg}</p>
)}
/>
</div>
);
}