MCPcopy Index your code
hub / github.com/dai-shi/react-hooks-async

github.com/dai-shi/react-hooks-async @v3.10.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v3.10.1 ↗ · + Follow
80 symbols 207 edges 46 files 0 documented · 0% updated 3y ago★ 4943 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

react-hooks-async

Build Status npm version bundle size

React custom hooks for async functions with abortability and composability

Introduction

JavaScript promises are not abortable/cancelable. However, DOM provides AbortController which can be used for aborting promises in general.

This is a library to provide an easy way to handle abortable async functions with React Hooks API.

It comes with a collection of custom hooks that can be used as is. More custom hooks can be developed based on core hooks.

Install

npm install react-hooks-async

Usage

A basic async example (run immediately)

import React from 'react';

import { useAsyncTask, useAsyncRun } from 'react-hooks-async';

const fetchStarwarsHero = async ({ signal }, id) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal });
  const data = await response.json();
  return data;
};

const StarwarsHero = ({ id }) => {
  const task = useAsyncTask(fetchStarwarsHero);
  useAsyncRun(task, id);
  const { pending, error, result, abort } = task;
  if (pending) return 

Loading...<button onClick={abort}>Abort</button>

;
  if (error) return 

Error: {error.name} {error.message}

;
  return 

Name: {result.name}

;
};

const App = () => (



    <StarwarsHero id={'1'} />
    <StarwarsHero id={'2'} />



);

A basic async example (run in callback)

import React, { useState } from 'react';

import { useAsyncTask } from 'react-hooks-async';

const fetchStarwarsHero = async ({ signal }, id) => {
  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal });
  const data = await response.json();
  return data;
};

const StarwarsHero = () => {
  const { start, started, result } = useAsyncTask(fetchStarwarsHero);
  const [id, setId] = useState('');
  return (



      <input value={id} onChange={e => setId(e.target.value)} />
      <button type="button" onClick={() => start(id)}>Fetch</button>
      {started && 'Fetching...'}


Name: {result && result.name}





  );
};

const App = () => (



    <StarwarsHero />
    <StarwarsHero />



);

A simple fetch example

import React from 'react';

import { useFetch } from 'react-hooks-async';

const UserInfo = ({ id }) => {
  const url = `https://reqres.in/api/users/${id}?delay=1`;
  const { pending, error, result, abort } = useFetch(url);
  if (pending) return 

Loading...<button onClick={abort}>Abort</button>

;
  if (error) return 

Error: {error.name} {error.message}

;
  return 

First Name: {result.data.first_name}

;
};

const App = () => (



    <UserInfo id={'1'} />
    <UserInfo id={'2'} />



);

A typeahead search example using combination

Preview

import React, { useState, useCallback } from 'react';

import {
  useAsyncCombineSeq,
  useAsyncRun,
  useAsyncTaskDelay,
  useAsyncTaskFetch,
} from 'react-hooks-async';

const Err = ({ error }) => 

Error: {error.name} {error.message}

;

const Loading = ({ abort }) => 

Loading...<button onClick={abort}>Abort</button>

;

const GitHubSearch = ({ query }) => {
  const url = `https://api.github.com/search/repositories?q=${query}`;
  const delayTask = useAsyncTaskDelay(500);
  const fetchTask = useAsyncTaskFetch(url);
  const combinedTask = useAsyncCombineSeq(delayTask, fetchTask);
  useAsyncRun(combinedTask);
  if (delayTask.pending) return 

Waiting...

;
  if (fetchTask.error) return <Err error={fetchTask.error} />;
  if (fetchTask.pending) return <Loading abort={fetchTask.abort} />;
  return (
    <ul>
      {fetchTask.result.items.map(({ id, name, html_url }) => (
        <li key={id}><a target="_blank" href={html_url}>{name}</a></li>
      ))}
    </ul>
  );
};

const App = () => {
  const [query, setQuery] = useState('');
  return (



      Query:
      <input value={query} onChange={e => setQuery(e.target.value)} />
      {query && <GitHubSearch query={query} />}



  );
};

Examples

The examples folder contains working examples. You can run one of them with

PORT=8080 npm run examples:01_minimal

and open http://localhost:8080 in your web browser.

You can also try them in codesandbox.io: 01 02 03 04 05 06 07 08 09 10

Reference

Note: Almost all hooks check referential equality of arguments. Arguments must be memoized if they would change in re-renders. Consider defining them outside of render, or useMemo/useMemoOne/useCallback/useCallbackOne.

States

State Description
started Initial false. Becomes true once the task is started. Becomes false when the task ends
pending Initial true. Stays true after the task is started. Becomes false when the task ends

An example, * initial: started=false, pending=true * first start: started=true, pending=true * first end: started=false, pending=false * second start: started=true, pending=true * second end: started=false, pending=false

Core hooks

useAsyncTask

const task = useAsyncTask(func);

This function is to create a new async task.

The first argument func is a function with an argument which is AbortController. This function returns a promise, but the function is responsible to cancel the promise by AbortController. If func receives the second or rest arguments, those can be passed by useAsyncRun(task, ...args) or task.start(...args).

When func is referentially changed, a new async task will be created.

The return value task is an object that contains information about the state of the task and some internal information. The state of the task can be destructured like the following:

const { pending, error, result } = task;

When a task is created, it's not started. To run a task, either call useAsyncRun(task, [...args]) in render, or call task.start([...args]) in callback.

useAsyncRun

useAsyncRun(task, ...args);

This function is to run an async task. When the task is updated, this function aborts the previous running task and start the new one.

The first argument task is an object returned by useAsyncTask and its variants. This can be a falsy value and in that case it won't run any tasks. Hence, it's possible to control the timing by:

useAsyncRun(ready && task);

The second or rest arguments are optional. If they are provided, the referential equality matters, so useMemo/useMemoOne would be necessary.

The return value of this function is void. You need to keep using task to get the state of the task.

Combining hooks

useAsyncCombineSeq

const combinedTask = useAsyncCombineSeq(task1, task2, ...);

This function combines multiple tasks in a sequential manner.

The arguments task1, task2, ... are tasks created by useAsyncTask. They shouldn't be started.

The return value combinedTask is a newly created combined task which holds an array of each task results in the result property.

useAsyncCombineAll

const combinedTask = useAsyncCombineAll(task1, task2, ...);

This function combines multiple tasks in a parallel manner.

The arguments and return value are the same as useAsyncCombineSeq.

useAsyncCombineRace

const combinedTask = useAsyncCombineRace(task1, task2, ...);

This function combines multiple tasks in a "race" manner.

The arguments and return value are the same as useAsyncCombineSeq.

Helper hooks

These hooks are just wrappers of useAsyncTask.

useAsyncTaskTimeout

const task = useAsyncTaskTimeout(func, delay);

This function returns an async task that runs func after delay ms.

When func is referentially changed, a new async task will be created.

useAsyncTaskDelay

const task = useAsyncTaskDelay(delay);

This function returns an async task that finishes after delay. This is a simpler variant of useAsyncTaskTimeout. delay is either a number or a function that returns a number.

When delay is referentially changed, a new async task will be created.

useAsyncTaskFetch

const task = useAsyncTaskFetch(input, init, bodyReader);

This function returns an async task that runs fetch. The first argument input and the second argument init are simply fed into fetch. The third argument bodyReader is to read the response body, which defaults to JSON parser.

When input or other arguments is referentially changed, a new async task will be created.

The hook useFetch has the same signature and runs the async task immediately.

useAsyncTaskAxios

const task = useAsyncTaskAxios(axios, config);

This is similar to useAsyncTaskFetch but using axios.

When config or other arguments is referentially changed, a new async task will be created.

The hook useAxios has the same signature and runs the async task immediately.

useAsyncTaskWasm

const task = useAsyncTaskWasm(input, importObject);

This function returns an async task that fetches wasm and creates a WebAssembly instance. The first argument input is simply fed into fetch. The second argument importObject is passed at instantiating WebAssembly.

When input or other arguments is referentially changed, a new async task will be created.

The hook useWasm has the same signature and runs the async task immediately.

Limitations

  • Due to the nature of React Hooks API, creating async tasks dynamically is not possible. For example, we cannot create arbitrary numbers of async tasks at runtime. For such a complex use case, we would use other solutions including upcoming react-cache and Suspense.

Blogs

Core symbols most depended-on inside this repo

useAsyncRun
called by 14
src/use-async-run.js
useAsyncTaskTimeout
called by 12
src/use-async-task-timeout.js
useMemoList
called by 10
src/utils.js
useAsyncTask
called by 10
src/use-async-task.js
useAsyncTaskFetch
called by 7
src/use-async-task-fetch.js
useAsyncTaskAxios
called by 2
src/use-async-task-axios.js
createFetchError
called by 1
src/use-async-task-fetch.js
safeReadBody
called by 1
src/use-async-task-fetch.js

Shape

Function 80

Languages

TypeScript100%

Modules by API surface

examples/07_race/src/DelayedData.tsx7 symbols
examples/06_progress/src/DelayedData.tsx7 symbols
src/use-async-task-fetch.js5 symbols
src/use-async-task.js4 symbols
examples/01_minimal/src/index.js4 symbols
__tests__/02_conditional_spec.js4 symbols
__tests__/01_basic_spec.js4 symbols
examples/08_wasm/src/CalcFib.tsx3 symbols
examples/05_axios/src/DisplayRemoteData.tsx3 symbols
examples/04_typeahead/src/GitHubSearch.tsx3 symbols
examples/03_startbutton/src/UserInfo.tsx3 symbols
examples/03_startbutton/src/DisplayRemoteData.tsx3 symbols

For agents

$ claude mcp add react-hooks-async \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact