Skip to content

Drive a query from input ​

Prerequisites ​

Use the React installation and mounting instructions in the runnable example. This guide adds a different application endpoint: POST /api/users/search accepts { "name": "Ada" } and returns a JSON array such as [{ "id": "u-ada", "name": "Ada" }]. Empty name returns all users. Implement it or adapt the URL/body to your service; the existing GET-only Storybook fixture does not provide it.

Add the query component ​

Save the following as src/UserSearch.tsx and mount <UserSearch /> using the same React entry pattern:

tsx
import { useState } from 'react';
import { Fetcher } from '@ahoo-wang/fetcher';
import { useFetcherQuery } from '@ahoo-wang/fetcher-react';

const api = new Fetcher({ baseURL: '/api' });
type User = { id: string; name: string };

export function UserSearch() {
  const [query, setQuery] = useState({ name: '' });
  const search = useFetcherQuery<{ name: string }, User[]>({
    fetcher: api,
    url: '/users/search',
    query,
  });
  return (
    <section>
      <label>
        Name
        <input
          value={query.name}
          onChange={event => setQuery({ name: event.target.value })}
        />
      </label>
      <button onClick={search.abort}>Cancel</button>
      <output aria-live="polite">
        {search.loading
          ? 'Loading'
          : search.error
            ? String(search.error)
            : search.result?.map(user => user.name).join(', ')}
      </output>
    </section>
  );
}

useFetcherQuery sends the query object as a POST body and defaults to JSON extraction. It is not a GET hook that appends the query to the URL. Use useQuery with your own executor when a different transport or service method is required; pass its second AbortController argument to the real operation.

Verify query changes ​

On mount the empty-name query loads all users. Enter Ada and inspect the network request: method POST, URL /api/users/search, body { "name": "Ada" }. The output should contain Ada. Delay the first response and change the name; only the latest execution may publish its result.

The query is controlled: it lives in your useState (or a URL, or a parent's state) and the hook re-executes when its content changes. Content is compared deeply, so recreating an equal object on every render does not resend. autoExecute defaults to true. For an Apply button, set autoExecute: false, update the query, then call execute() to send the current query. Pass query: undefined while a required input is missing: nothing is sent until it is defined. Neither undefined nor autoExecute: false cancels work already running; call abort() for that. undefined is the only "not ready" value; validate business fields before passing a query.

Failure and lifetime ​

Return HTTP 500 from the application endpoint and confirm the output renders the hook error. Invalid response JSON also becomes an execution error. Cancelling or unmounting invalidates the current execution; only operations connected to the controller can stop their actual work. Results are component-local, without global cache invalidation or cross-component request deduplication.

Continue with debouncing, query reference, and resource ownership.

Released under the Apache License 2.0.