alepha@docs:~/framework/docs/reference/react-hooks$
cat useAction.md | pretty
2 min read
Last commit:

#useAction

#Import

typescript
1import { useAction } from "alepha/react";

#Overview

Hook for handling async actions with automatic error handling and event emission.

The handler takes an ActionContext FIRST, then the arguments run() was given: handler: async ({ signal }, id: string) => ....

run() never rejects. It resolves one RunResult:

  • { ok: true, value }: the handler returned value.
  • { ok: false, reason: "error", error }: the handler threw error (also in error state and emitted as react:action:error).
  • { ok: false, reason: "dropped" }: refused because a run was already in flight, or a debounced call replaced by a newer one.
  • { ok: false, reason: "aborted" }: cancelled by cancel(), an unmount or a newer run.

So a follow-up branches on the result: if ((await save.run()).ok) close().

By default, prevents concurrent executions - if an action is running and you call it again, the second call is dropped. Use debounce option to delay execution instead.

Emits lifecycle events:

  • react:action:begin - When action starts
  • react:action:success - When action completes successfully
  • react:action:error - When action throws an error
  • react:action:end - Always emitted at the end

#Examples

Basic usage

tsx
 1const action = useAction( 2  { 3    handler: async (_ctx, data: Data) => { 4      await api.save(data); 5    }, 6  }, 7  [], 8); 9 10<button onClick={() => action.run(data)} disabled={action.loading}>11  Save12</button>;

With debounce (search input)

tsx
 1const search = useAction( 2  { 3    handler: async (_ctx, query: string) => { 4      await api.search(query); 5    }, 6    debounce: 300, // Wait 300ms after last call 7  }, 8  [], 9);10 11<input onChange={(e) => search.run(e.target.value)} />;

Run on component mount

tsx
 1const fetchData = useAction( 2  { 3    handler: async () => { 4      const data = await api.getData(); 5      return data; 6    }, 7    runOnInit: true, // Runs once when component mounts 8  }, 9  [],10);

Run periodically (polling)

tsx
 1const pollStatus = useAction( 2  { 3    handler: async () => { 4      const status = await api.getStatus(); 5      return status; 6    }, 7    runEvery: 5000, // Run every 5 seconds 8  }, 9  [],10);11 12// Or with duration tuple13const pollStatus = useAction(14  {15    handler: async () => {16      const status = await api.getStatus();17      return status;18    },19    runEvery: [30, "seconds"], // Run every 30 seconds20  },21  [],22);

With AbortController

tsx
 1const fetch = useAction( 2  { 3    handler: async ({ signal }, url: string) => { 4      const response = await fetch(url, { signal }); 5      return response.json(); 6    }, 7  }, 8  [], 9);10// Automatically cancelled on unmount or when new request starts

With error handling

tsx
 1const deleteAction = useAction( 2  { 3    handler: async (_ctx, id: string) => { 4      await api.delete(id); 5    }, 6    onError: (error) => { 7      if (error.code === "NOT_FOUND") { 8        // Custom error handling 9      }10    },11  },12  [],13);14 15{16  deleteAction.error && <div>Error: {deleteAction.error.message}</div>;17}

Global error handling

tsx
1// In your root app setup2alepha.events.on("react:action:error", ({ error }) => {3  toast.danger(error.message);4  Sentry.captureException(error);5});