react-action-z
v0.0.19
Published
Turn async functions into UI actions. Handle loading, error, retry, and concurrency automatically.
Maintainers
Readme
⚡ react-action-z
LIVE EXAMPLE
Unified async action layer for React.
Run async functions as actions, not boilerplate. Actions run only when you call run() — never automatically.
Why react-action-z?
- ⚡ Unified async actions
- 🧠 Built-in loading / error state
- 🔁 Retry support
- 🧩 Middleware system
- 🌍 Global action state
- 🧾 Full TypeScript support
Traditional async UI code:
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
async function login(data) {
try {
setLoading(true)
const result = await api.login(data)
} catch (err) {
setError(err)
} finally {
setLoading(false)
}
}React Action:
const login = useAction(api.login)
await login.run(data)UI state automatically available:
login.loading
login.data
login.errorInstallation
npm install react-action-zBasic Usage
import { useAction } from "react-action-z"
const login = useAction(api.login)
await login.run({
email,
password
})State is automatically managed:
login.loading
login.data
login.errorUI example:
<button disabled={login.loading}>
{login.loading ? "Loading..." : "Login"}
</button>Example with JSONPlaceholder
Example using the free API from JSONPlaceholder.
Fetch a user from:
https://jsonplaceholder.typicode.com/users/1API
async function fetchUser(id: number) {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${id}`
)
if (!res.ok) {
throw new Error("Failed to fetch user")
}
return res.json()
}React Component
import React from "react"
import { useAction } from "react-action-z"
async function fetchUser(id: number) {
const res = await fetch(
`https://jsonplaceholder.typicode.com/users/${id}`
)
if (!res.ok) {
throw new Error("Failed to fetch user")
}
return res.json()
}
export default function UserExample() {
const getUser = useAction(fetchUser)
return (
<div>
<button
onClick={() => getUser.run(1)}
disabled={getUser.loading}
>
{getUser.loading ? "Loading..." : "Load User"}
</button>
{getUser.error && (
<p>Error loading user</p>
)}
{getUser.data && (
<div style={{ marginTop: 20 }}>
<h3>{getUser.data.name}</h3>
<p>{getUser.data.email}</p>
<p>{getUser.data.phone}</p>
</div>
)}
</div>
)
}Result
Click the button:
Load UserThe action automatically manages:
loading
data
errorExample returned data:
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "[email protected]",
"phone": "1-770-736-8031"
}Action API
const action = useAction(fn, options)Return value:
action.run(...)
action.loading
action.data
action.error
action.reset()Options
useAction(api.login, {
retry: 2,
strategy: "replace",
key: "login",
optimistic: (data) => {},
onSuccess: (data) => {},
onError: (err) => {}
})| option | description | |-------------|---------------------| | retry | retry on failure | | strategy | concurrency control | | key | global action key | | optimistic | optimistic update | | onSuccess | success callback | | onError | error callback |
Concurrency Strategy
useAction(api.save, {
strategy: "ignore"
})| strategy | behavior | |----------|-----------------------------------| | replace | only latest result updates state | | ignore | ignore if running | | parallel | allow multiple runs |
Example:
const save = useAction(api.save, {
strategy: "ignore"
})Prevent double submit.
Retry
Automatically retry failed requests.
const fetchUser = useAction(api.fetchUser, {
retry: 2
})Behavior:
attempt 1
attempt 2
attempt 3Optimistic Update
Apply UI update before request finishes.
const updateUser = useAction(api.updateUser, {
optimistic: (data) => {
setUser(data)
}
})This improves UI responsiveness.
Global Action State
Actions can share state across components.
import { useAction } from "react-action-z"
const login = useAction(api.login, {
key: "login"
})
export function LoginButton() {
return (
<button
disabled={login.loading}
onClick={() => login.run({ email, password })}
>
{login.loading ? "Logging in..." : "Login"}
</button>
)
}Access anywhere:
// GlobalSpinner.tsx
import { useGlobalAction } from "react-action-z"
export function GlobalSpinner() {
const login = useGlobalAction("login")
if (!login.loading) return null
return <div>Authenticating...</div>
}Result:
- LoginButton triggers action
- GlobalSpinner reacts automatically
Middleware
React Action supports middleware similar to Redux.
import { createActionClient } from "react-action-z"
createActionClient({
middleware: [
async (context, next) => {
console.log("action:", context.key)
return next()
}
]
})Middleware context:
{
key?: string
args: any[]
}Use cases:
- logging
- analytics
- metrics
- debugging
Reset State
Reset action state manually.
login.reset()Result:
{
loading: false
data: null
error: null
}Example
const createPost = useAction(api.createPost, {
retry: 1
})
async function handleSubmit(data) {
await createPost.run(data)
}UI:
<button disabled={createPost.loading}>
Publish
</button>
{createPost.error && <p>Error</p>}Comparison
| Criteria | react-action-z | React state | |---------------------|----------------|-------------| | Async state | ✅ | manual | | Retry | ✅ | manual | | Global action state | ✅ | ❌ | | Middleware | ✅ | ❌ | | Optimistic update | ✅ | manual | | Boilerplate | minimal | high |
Architecture
Component
↓
useAction()
↓
Action Runner
↓
Middleware
↓
Async Function
↓
State UpdatePhilosophy
React Action treats async functions as first-class UI actions.
Instead of manually managing:
- loading
- error
- retries
- concurrency
You simply run actions:
action.run()License
MIT
