Skip to content

Why AsyncFlowState?

The Before & After

Here is a common scenario: submitting a form and handling the API response.

Before — Manual State Management

tsx
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

const handleSubmit = async (data) => {
  setLoading(true);
  setError(null);
  try {
    await api.save(data);
    alert("Saved!");
  } catch (err) {
    setError(err);
  } finally {
    setLoading(false);
  }
};

return (
  <button onClick={handleSubmit} disabled={loading}>
    {loading ? "Saving..." : "Save"}
  </button>
);

Problems with this approach:

  • No double-click prevention (what if api.save is slow?)
  • No retry logic
  • No accessibility attributes
  • Must repeat this pattern for every async action
  • Error handling is easy to forget

After — With AsyncFlowState

tsx
const flow = useFlow(api.save, {
  onSuccess: () => alert("Saved!"),
});

return (
  <button {...flow.button()}>{flow.loading ? "Saving..." : "Save"}</button>
);

What you get for free:

  • Double submission prevention
  • Automatic disabled and aria-busy attributes
  • Consistent loading state management
  • Clean error handling
  • Zero boilerplate

Comparison with Alternatives

vs. React Query / SWR

React Query / SWRAsyncFlowState
PurposeData fetching & cachingAction behavior & UX
FocusGET requests, stale dataPOST/PUT/DELETE actions
CacheSmart caching built-inNo cache (not needed)
Use withReading dataWriting data, user actions
Works with Complements each other Complements each other

vs. Redux / Zustand

Redux / ZustandAsyncFlowState
PurposeGlobal state managementAction behavior management
ScopeEntire app stateIndividual async actions
ComplexityActions, reducers, middlewareOne hook, zero config
Learning curveModerate to steepMinimal
Works with Complements each other Complements each other

vs. Manual useState

Manual useStateAsyncFlowState
BoilerplateHigh — repeated everywhereNone
Double submissionMust implement manuallyAutomatic
Retry logicMust implement manuallyOne config
Optimistic UIComplex implementationOne line
AccessibilityUsually forgottenBuilt-in
Error focusMust implement manuallyAutomatic

When to Use AsyncFlowState

Use AsyncFlowState when you have:

  • Form submissions — Login, registration, profile updates
  • CRUD operations — Create, update, delete buttons
  • File uploads — With progress tracking and retry
  • Payment flows — Where double-submission is catastrophic
  • Multi-step workflows — Sequential or parallel operations
  • Search with debounce — Controlled async search inputs
  • Any button that calls an API — The universal use case

When NOT to use AsyncFlowState

  • Simple data fetching on mount → Use React Query / SWR
  • Global application state → Use Redux / Zustand / Pinia
  • Real-time subscriptions → Use dedicated WebSocket/SSE libraries

Built with by AsyncFlowState Contributors
Open Source · MIT License