Skip to content
Back to skills

Fetcher React Hooks

ASecurity

Drive React component state from requests with `@ahoo-wang/fetcher-react` 6: `useFetcher`, `useFetcherQuery`, `useQuery`, `useExecutePromise`, debounced variants, `useKeyStorage`, `useEventSubscription`, CoSec `SecurityProvider`/`RouteGuard`, and hooks generated from decorator services. Use for loading/error state, cancellation, debounced search, protected routes. Do not load when a hook or option stopped existing after upgrading from 5.x: that is fetcher-v6-migration. CoSec token setup: fetc...

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentsgoreactapisecurity

Works with

  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add Ahoo-Wang/skills --skill fetcher-react-hooks --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fetcher React Hooks?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fetcher React Hooks
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ahoo-wang-fetcher-react-hooks/badge)](https://www.skillsdirectory.com/skills/ahoo-wang-fetcher-react-hooks)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fetcher-react-hooks
description: >
  Drive React component state from requests with `@ahoo-wang/fetcher-react` 6: `useFetcher`, `useFetcherQuery`, `useQuery`, `useExecutePromise`, debounced variants, `useKeyStorage`, `useEventSubscription`, CoSec `SecurityProvider`/`RouteGuard`, and hooks generated from decorator services. Use for loading/error state, cancellation, debounced search, protected routes. Do not load when a hook or option stopped existing after upgrading from 5.x: that is fetcher-v6-migration. CoSec token setup: fetcher-cosec-auth.
---

# fetcher-react-hooks

## Decisions

- **Pick the lowest layer that fits**: `usePromiseState` (raw state) → `useExecutePromise` (execute/abort/reset, unmount-safe) → `useFetcher` (one Fetcher request) / `useQuery` (your own query function) → `useFetcherQuery` (POST a query object). Debounced variants wrap each.
- **Queries are controlled**: keep the query in your own React state and pass it as `query`; the hook executes whenever its content changes (deep-equal, so an inline object literal does not re-run). `query: undefined` means "not ready": nothing runs.
- **Entry points**: the root entry has everything; `@ahoo-wang/fetcher-react/core` (state, query, debounce hooks) and `@ahoo-wang/fetcher-react/fetcher` (`useFetcher`, `useFetcherQuery` and their debounced forms) are ESM-only subpaths without CoSec, storage or event-bus code.
- **From a decorator service**: `createQueryApiHooks({ api: service })` or `createExecuteApiHooks({ api: service })` generate `use<Method>` hooks instead of hand-written wrappers.

## Gotchas a capable model gets wrong

- `execute` takes a `PromiseSupplier` — `(abortController) => promise` — never a promise. It **never rejects**: it resolves to `{ status, result, error }`, the state this execution ended in (`status: 'idle'` when it was cancelled or the component unmounted). Branch on `status`; do not wrap it in `try/catch`.
- Only the latest execution writes state: a new `execute` aborts the previous one, as do `abort()`, `reset()` and unmounting. `abort()` cancels an in-flight request (a settled result stays); `reset()` cancels and clears back to `idle`.
- `useFetcher`'s `result` is the whole `FetchExchange` unless you pass `resultExtractor: ResultExtractors.Json`; `useFetcherQuery` defaults to JSON and sends `POST url` with the query as the body. `exchange` is the exchange behind `result`, or behind `error` when it is an `ExchangeError` — read `exchange?.response?.status` of a 404 there.
- `useFetcher` owns cancellation: it sends `{ ...request, abortController }`, so an `abortController` you put on the request is replaced; cancel with `abort()`, or pass `request.signal`.
- Query hooks' `execute()` takes no arguments and re-runs the current query. `useQuery`'s `execute` option is `(query, abortController) => Promise<R>`. An auto-executing query renders `loading` on its first render.
- `useDebouncedQuery` / `useDebouncedFetcherQuery` follow the controlled `query`: the first query runs at once, later changes after `debounce.delay`. They return `pending` (a boolean) and `flush()` (apply the waiting query now). `useDebouncedValue(value, { delay })` debounces any value. `useDebouncedCallback`, `useDebouncedExecutePromise` and `useDebouncedFetcher` instead return `run(...args)`, `cancel()` and `isPending()`.
- Generated query hooks take `{ query, attributes, autoExecute }` and call `method(query, attributes, abortController)`. That fits any one-input method, `getUser(@path('id') id)` included (a decorator method ignores the undecorated extra argument and finds the controller anywhere), so a read that follows an id passes `{ query: id }` to its query hook, not an id to an execute hook from an effect. Generated execute hooks pass the controller to the method only when the generated hook itself is called with `appendAbortController: true` (a hook option: the factories take only `{ api }`); otherwise `abort()` only drops the state update.
- `useEventSubscription` subscribes once per `bus` and handler `name`; it calls the latest `handle`, so an inline handler is fine.
- CoSec in React: wrap the app in `SecurityProvider` with the **same** `TokenStorage` instance the `CoSecConfigurer` uses (a second `new TokenStorage()` keeps its own cache and misses sign-ins until reload). Read `authenticated` / `currentUser` / `signIn` / `signOut` from `useSecurityContext()`. `RouteGuard`'s `onUnauthorized` runs in an effect after commit, so `navigate('/login')` there is safe; never navigate during render. `RefreshableRouteGuard` takes `configurer.tokenManager`.

## Minimal example

```tsx
import { useState } from 'react';
import { useDebouncedFetcherQuery } from '@ahoo-wang/fetcher-react';

export function Search() {
  const [query, setQuery] = useState({ keyword: '' });
  const { loading, result, error, pending } = useDebouncedFetcherQuery<
    { keyword: string },
    { items: string[] }
  >({ url: '/api/search', query, debounce: { delay: 300 } });
  return (
    <>
      <input
        value={query.keyword}
        onChange={e => setQuery({ keyword: e.target.value })}
      />
      {error ? <p>{error.message}</p> : null}
      {loading || pending ? '…' : result?.items.join(', ')}
    </>
  );
}
```

```tsx
const { execute } = useExecutePromise<Order>();
const onSubmit = async () => {
  const { status, error } = await execute(ac => api.placeOrder(form, ac));
  if (status === 'success') navigate('/done');
  else if (status === 'error') toast(error?.message);
};
```

```tsx
import { useNavigate } from 'react-router';
import { RouteGuard, SecurityProvider } from '@ahoo-wang/fetcher-react';
import { tokenStorage } from './http'; // the TokenStorage given to CoSecConfigurer

export function App() {
  const navigate = useNavigate();
  return (
    <SecurityProvider tokenStorage={tokenStorage}>
      <RouteGuard onUnauthorized={() => navigate('/login')}>
        <Dashboard />
      </RouteGuard>
    </SecurityProvider>
  );
}
```

## References

- `references/api.md`: hook signatures and return fields, `PromiseStatus` transitions, cancellation rules, debounced hooks, storage and event hooks, API hook generation and CoSec components. Load it for exact options.

## Related Skills

- $fetcher-integration: the Fetcher, interceptors and result extractors underneath.
- $fetcher-decorator-service: services that `createExecuteApiHooks` / `createQueryApiHooks` wrap.
- $fetcher-storage: `KeyStorage` behind `useKeyStorage`.
- $fetcher-eventbus: buses behind `useEventSubscription`.
- $fetcher-cosec-auth: tokens behind `SecurityProvider`.
- $fetcher-v6-migration: upgrading 5.x hook code and hooks removed in 6.0.

Files in this skill

  • SKILL.md1.8 KB
  • agents/openai.yaml197 B
  • references/api.md12.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.