{"id":13450428,"url":"https://github.com/dai-shi/react-hooks-async","last_synced_at":"2025-05-16T15:09:48.371Z","repository":{"id":33755209,"uuid":"161332389","full_name":"dai-shi/react-hooks-async","owner":"dai-shi","description":"[NOT MAINTAINED] React custom hooks for async functions with abortability and composability ","archived":false,"fork":false,"pushed_at":"2023-03-04T03:01:51.000Z","size":4348,"stargazers_count":495,"open_issues_count":22,"forks_count":26,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-07T08:01:49.713Z","etag":null,"topics":["abortable","abortcontroller","async","hooks-api-react","promise","react","react-hooks"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/react-hooks-async","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dai-shi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-12-11T12:44:23.000Z","updated_at":"2024-09-24T03:40:16.000Z","dependencies_parsed_at":"2024-01-07T18:06:31.805Z","dependency_job_id":"dd5b15b2-3e86-438d-82c8-1fa63a1143b6","html_url":"https://github.com/dai-shi/react-hooks-async","commit_stats":{"total_commits":209,"total_committers":6,"mean_commits":"34.833333333333336","dds":0.08133971291866027,"last_synced_commit":"ad5801e151feff216e622e64cf004c85f1ae80d2"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-hooks-async","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-hooks-async/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-hooks-async/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dai-shi%2Freact-hooks-async/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dai-shi","download_url":"https://codeload.github.com/dai-shi/react-hooks-async/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553959,"owners_count":22090417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abortable","abortcontroller","async","hooks-api-react","promise","react","react-hooks"],"created_at":"2024-07-31T07:00:34.564Z","updated_at":"2025-05-16T15:09:43.364Z","avatar_url":"https://github.com/dai-shi.png","language":"JavaScript","funding_links":[],"categories":["Packages"],"sub_categories":[],"readme":"This project is no longer actively maintained.\nThe approach this project takes is so-called useEffect chaining,\nwhich will not be a best pracitce in the future versions of React.\nThis would be still useful for learning and might work for small projects.\nReact community should move to new data fetching approach,\nor at least approach to fire a single async function in useEffect.\nSee also: https://github.com/dai-shi/react-hooks-async/issues/64\n\n---\n\n# react-hooks-async\n\n[![Build Status](https://travis-ci.com/dai-shi/react-hooks-async.svg?branch=master)](https://travis-ci.com/dai-shi/react-hooks-async)\n[![npm version](https://badge.fury.io/js/react-hooks-async.svg)](https://badge.fury.io/js/react-hooks-async)\n[![bundle size](https://badgen.net/bundlephobia/minzip/react-hooks-async)](https://bundlephobia.com/result?p=react-hooks-async)\n\nReact custom hooks for async functions with abortability and composability\n\n## Introduction\n\nJavaScript promises are not abortable/cancelable.\nHowever, DOM provides AbortController which can be\nused for aborting promises in general.\n\nThis is a library to provide an easy way to handle\nabortable async functions with React Hooks API.\n\nIt comes with a collection of custom hooks that can be used as is.\nMore custom hooks can be developed based on core hooks.\n\n## Install\n\n```bash\nnpm install react-hooks-async\n```\n\n## Usage\n\n### A basic async example (run immediately)\n\n```jsx\nimport React from 'react';\n\nimport { useAsyncTask, useAsyncRun } from 'react-hooks-async';\n\nconst fetchStarwarsHero = async ({ signal }, id) =\u003e {\n  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal });\n  const data = await response.json();\n  return data;\n};\n\nconst StarwarsHero = ({ id }) =\u003e {\n  const task = useAsyncTask(fetchStarwarsHero);\n  useAsyncRun(task, id);\n  const { pending, error, result, abort } = task;\n  if (pending) return \u003cdiv\u003eLoading...\u003cbutton onClick={abort}\u003eAbort\u003c/button\u003e\u003c/div\u003e;\n  if (error) return \u003cdiv\u003eError: {error.name} {error.message}\u003c/div\u003e;\n  return \u003cdiv\u003eName: {result.name}\u003c/div\u003e;\n};\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cStarwarsHero id={'1'} /\u003e\n    \u003cStarwarsHero id={'2'} /\u003e\n  \u003c/div\u003e\n);\n```\n\n### A basic async example (run in callback)\n\n```jsx\nimport React, { useState } from 'react';\n\nimport { useAsyncTask } from 'react-hooks-async';\n\nconst fetchStarwarsHero = async ({ signal }, id) =\u003e {\n  const response = await fetch(`https://swapi.co/api/people/${id}/`, { signal });\n  const data = await response.json();\n  return data;\n};\n\nconst StarwarsHero = () =\u003e {\n  const { start, started, result } = useAsyncTask(fetchStarwarsHero);\n  const [id, setId] = useState('');\n  return (\n    \u003cdiv\u003e\n      \u003cinput value={id} onChange={e =\u003e setId(e.target.value)} /\u003e\n      \u003cbutton type=\"button\" onClick={() =\u003e start(id)}\u003eFetch\u003c/button\u003e\n      {started \u0026\u0026 'Fetching...'}\n      \u003cdiv\u003eName: {result \u0026\u0026 result.name}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cStarwarsHero /\u003e\n    \u003cStarwarsHero /\u003e\n  \u003c/div\u003e\n);\n```\n\n### A simple fetch example\n\n```jsx\nimport React from 'react';\n\nimport { useFetch } from 'react-hooks-async';\n\nconst UserInfo = ({ id }) =\u003e {\n  const url = `https://reqres.in/api/users/${id}?delay=1`;\n  const { pending, error, result, abort } = useFetch(url);\n  if (pending) return \u003cdiv\u003eLoading...\u003cbutton onClick={abort}\u003eAbort\u003c/button\u003e\u003c/div\u003e;\n  if (error) return \u003cdiv\u003eError: {error.name} {error.message}\u003c/div\u003e;\n  return \u003cdiv\u003eFirst Name: {result.data.first_name}\u003c/div\u003e;\n};\n\nconst App = () =\u003e (\n  \u003cdiv\u003e\n    \u003cUserInfo id={'1'} /\u003e\n    \u003cUserInfo id={'2'} /\u003e\n  \u003c/div\u003e\n);\n```\n\n### A typeahead search example using combination\n\n\u003cimg src=\"./examples/04_typeahead/screencast.gif\" alt=\"Preview\" width=\"350\" /\u003e\n\n```jsx\nimport React, { useState, useCallback } from 'react';\n\nimport {\n  useAsyncCombineSeq,\n  useAsyncRun,\n  useAsyncTaskDelay,\n  useAsyncTaskFetch,\n} from 'react-hooks-async';\n\nconst Err = ({ error }) =\u003e \u003cdiv\u003eError: {error.name} {error.message}\u003c/div\u003e;\n\nconst Loading = ({ abort }) =\u003e \u003cdiv\u003eLoading...\u003cbutton onClick={abort}\u003eAbort\u003c/button\u003e\u003c/div\u003e;\n\nconst GitHubSearch = ({ query }) =\u003e {\n  const url = `https://api.github.com/search/repositories?q=${query}`;\n  const delayTask = useAsyncTaskDelay(500);\n  const fetchTask = useAsyncTaskFetch(url);\n  const combinedTask = useAsyncCombineSeq(delayTask, fetchTask);\n  useAsyncRun(combinedTask);\n  if (delayTask.pending) return \u003cdiv\u003eWaiting...\u003c/div\u003e;\n  if (fetchTask.error) return \u003cErr error={fetchTask.error} /\u003e;\n  if (fetchTask.pending) return \u003cLoading abort={fetchTask.abort} /\u003e;\n  return (\n    \u003cul\u003e\n      {fetchTask.result.items.map(({ id, name, html_url }) =\u003e (\n        \u003cli key={id}\u003e\u003ca target=\"_blank\" href={html_url}\u003e{name}\u003c/a\u003e\u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n};\n\nconst App = () =\u003e {\n  const [query, setQuery] = useState('');\n  return (\n    \u003cdiv\u003e\n      Query:\n      \u003cinput value={query} onChange={e =\u003e setQuery(e.target.value)} /\u003e\n      {query \u0026\u0026 \u003cGitHubSearch query={query} /\u003e}\n    \u003c/div\u003e\n  );\n};\n```\n\n## Examples\n\nThe [examples](examples) folder contains working examples.\nYou can run one of them with\n\n```bash\nPORT=8080 npm run examples:01_minimal\n```\n\nand open \u003chttp://localhost:8080\u003e in your web browser.\n\nYou can also try them in codesandbox.io:\n[01](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/01_minimal)\n[02](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/02_typescript)\n[03](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/03_startbutton)\n[04](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/04_typeahead)\n[05](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/05_axios)\n[06](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/06_progress)\n[07](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/07_race)\n[08](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/08_wasm)\n[09](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/09_args)\n[10](https://codesandbox.io/s/github/dai-shi/react-hooks-async/tree/master/examples/10_pagination)\n\n## Reference\n\nNote: Almost all hooks check referential equality of arguments.\nArguments must be memoized if they would change in re-renders.\nConsider defining them outside of render,\nor useMemo/useMemoOne/useCallback/useCallbackOne.\n\n### States\n\n| State  | Description |\n| ------------- | ------------- |\n| started  | Initial _false_. Becomes _true_ once the task is started. Becomes _false_ when the task ends  |\n| pending  | Initial _true_.  Stays _true_ after the task is started. Becomes _false_ when the task ends |\n\nAn example,\n* initial: started=false, pending=true\n* first start: started=true, pending=true\n* first end: started=false, pending=false\n* second start: started=true, pending=true\n* second end: started=false, pending=false\n\n\n### Core hooks\n\n#### useAsyncTask\n\n```javascript\nconst task = useAsyncTask(func);\n```\n\nThis function is to create a new async task.\n\nThe first argument `func` is a function with an argument\nwhich is AbortController. This function returns a promise,\nbut the function is responsible to cancel the promise by AbortController.\nIf `func` receives the second or rest arguments, those can be passed by\n`useAsyncRun(task, ...args)` or `task.start(...args)`.\n\nWhen `func` is referentially changed, a new async task will be created.\n\nThe return value `task` is an object that contains information about\nthe state of the task and some internal information.\nThe state of the task can be destructured like the following:\n\n```javascript\nconst { pending, error, result } = task;\n```\n\nWhen a task is created, it's not started.\nTo run a task, either\ncall `useAsyncRun(task, [...args])` in render, or\ncall `task.start([...args])` in callback.\n\n#### useAsyncRun\n\n```javascript\nuseAsyncRun(task, ...args);\n```\n\nThis function is to run an async task.\nWhen the task is updated, this function aborts the previous running task\nand start the new one.\n\nThe first argument `task` is an object returned by `useAsyncTask`\nand its variants. This can be a falsy value and in that case\nit won't run any tasks. Hence, it's possible to control the timing by:\n\n```javascript\nuseAsyncRun(ready \u0026\u0026 task);\n```\n\nThe second or rest arguments are optional.\nIf they are provided, the referential equality matters,\nso useMemo/useMemoOne would be necessary.\n\nThe return value of this function is `void`.\nYou need to keep using `task` to get the state of the task.\n\n### Combining hooks\n\n#### useAsyncCombineSeq\n\n```javascript\nconst combinedTask = useAsyncCombineSeq(task1, task2, ...);\n```\n\nThis function combines multiple tasks in a sequential manner.\n\nThe arguments `task1`, `task2`, ... are tasks created by `useAsyncTask`.\nThey shouldn't be started.\n\nThe return value `combinedTask` is a newly created combined task which\nholds an array of each task results in the result property.\n\n#### useAsyncCombineAll\n\n```javascript\nconst combinedTask = useAsyncCombineAll(task1, task2, ...);\n```\n\nThis function combines multiple tasks in a parallel manner.\n\nThe arguments and return value are the same as `useAsyncCombineSeq`.\n\n#### useAsyncCombineRace\n\n```javascript\nconst combinedTask = useAsyncCombineRace(task1, task2, ...);\n```\n\nThis function combines multiple tasks in a \"race\" manner.\n\nThe arguments and return value are the same as `useAsyncCombineSeq`.\n\n### Helper hooks\n\nThese hooks are just wrappers of `useAsyncTask`.\n\n#### useAsyncTaskTimeout\n\n```javascript\nconst task = useAsyncTaskTimeout(func, delay);\n```\n\nThis function returns an async task that runs `func` after `delay` ms.\n\nWhen `func` is referentially changed, a new async task will be created.\n\n#### useAsyncTaskDelay\n\n```javascript\nconst task = useAsyncTaskDelay(delay);\n```\n\nThis function returns an async task that finishes after `delay`.\nThis is a simpler variant of `useAsyncTaskTimeout`.\n`delay` is either a number or a function that returns a number.\n\nWhen `delay` is referentially changed, a new async task will be created.\n\n#### useAsyncTaskFetch\n\n```javascript\nconst task = useAsyncTaskFetch(input, init, bodyReader);\n```\n\nThis function returns an async task that runs\n[fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch).\nThe first argument `input` and the second argument `init`\nare simply fed into `fetch`. The third argument `bodyReader`\nis to read the response body, which defaults to JSON parser.\n\nWhen `input` or other arguments is referentially changed, a new async task will be created.\n\nThe hook `useFetch` has the same signature and runs the async task immediately.\n\n#### useAsyncTaskAxios\n\n```javascript\nconst task = useAsyncTaskAxios(axios, config);\n```\n\nThis is similar to `useAsyncTaskFetch` but using\n[axios](https://github.com/axios/axios).\n\nWhen `config` or other arguments is referentially changed, a new async task will be created.\n\nThe hook `useAxios` has the same signature and runs the async task immediately.\n\n#### useAsyncTaskWasm\n\n```javascript\nconst task = useAsyncTaskWasm(input, importObject);\n```\n\nThis function returns an async task that fetches wasm\nand creates a WebAssembly instance.\nThe first argument `input` is simply fed into `fetch`.\nThe second argument `importObject` is passed at instantiating WebAssembly.\n\nWhen `input` or other arguments is referentially changed, a new async task will be created.\n\nThe hook `useWasm` has the same signature and runs the async task immediately.\n\n## Limitations\n\n- Due to the nature of React Hooks API, creating async tasks dynamically\n  is not possible. For example, we cannot create arbitrary numbers of\n  async tasks at runtime.\n  For such a complex use case, we would use other solutions including\n  upcoming react-cache and Suspense.\n\n## Blogs\n\n- [Introduction to abortable async functions for React with hooks](https://blog.axlight.com/posts/introduction-to-abortable-async-functions-for-react-with-hooks/)\n- [Developing React custom hooks for abortable async functions with AbortController](https://blog.axlight.com/posts/developing-react-custom-hooks-for-abortable-async-functions-with-abortcontroller/)\n- [How to create React custom hooks for data fetching with useEffect](https://blog.axlight.com/posts/how-to-create-react-custom-hooks-for-data-fetching-with-useeffect/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freact-hooks-async","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdai-shi%2Freact-hooks-async","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdai-shi%2Freact-hooks-async/lists"}