{"id":13433123,"url":"https://github.com/ava/use-http","last_synced_at":"2025-04-14T06:50:44.592Z","repository":{"id":37209785,"uuid":"180707102","full_name":"ava/use-http","owner":"ava","description":"🐶  React hook for making isomorphic http requests","archived":false,"fork":false,"pushed_at":"2024-02-07T16:10:12.000Z","size":8014,"stargazers_count":2313,"open_issues_count":89,"forks_count":115,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-04-14T02:33:35.237Z","etag":null,"topics":["fetch","fetch-data","graphql","http","isomorphic","mutation","query","react","react-cache","react-fetch-hook","react-hook","react-hooks","react-suspense","react-usefetch","reacthook","request","rest-client","ssr","suspense","usefetch"],"latest_commit_sha":null,"homepage":"https://use-http.com","language":"TypeScript","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/ava.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/contributing.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"alex-cory"}},"created_at":"2019-04-11T03:26:44.000Z","updated_at":"2025-04-10T15:10:54.000Z","dependencies_parsed_at":"2024-05-03T10:00:20.373Z","dependency_job_id":"291979bc-75d0-4c85-87f1-edf6666edfda","html_url":"https://github.com/ava/use-http","commit_stats":{"total_commits":612,"total_committers":41,"mean_commits":"14.926829268292684","dds":0.4771241830065359,"last_synced_commit":"031b2ee8e00241fc8585dda7cf0c135eda5a181a"},"previous_names":["alex-cory/use-http","alex-cory/react-usefetch"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ava%2Fuse-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ava%2Fuse-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ava%2Fuse-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ava%2Fuse-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ava","download_url":"https://codeload.github.com/ava/use-http/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248837274,"owners_count":21169373,"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":["fetch","fetch-data","graphql","http","isomorphic","mutation","query","react","react-cache","react-fetch-hook","react-hook","react-hooks","react-suspense","react-usefetch","reacthook","request","rest-client","ssr","suspense","usefetch"],"created_at":"2024-07-31T02:01:21.329Z","updated_at":"2025-04-14T06:50:44.570Z","avatar_url":"https://github.com/ava.png","language":"TypeScript","readme":"[![use-http logo][3]][5]\n\n\u003cbr/\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003euseFetch\u003c/h1\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ava/use-http/blob/master/.github/contributing.md\"\u003e\n    \u003cimg src=\"https://camo.githubusercontent.com/d4e0f63e9613ee474a7dfdc23c240b9795712c96/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://circleci.com/gh/ava/use-http\"\u003e\n    \u003cimg src=\"https://img.shields.io/circleci/project/github/ava/use-http/master.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/use-http\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dt/use-http.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://lgtm.com/projects/g/ava/use-http/context:javascript\"\u003e\n    \u003cimg alt=\"undefined\" src=\"https://img.shields.io/lgtm/grade/javascript/g/ava/use-http.svg?logo=lgtm\u0026logoWidth=18\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"http://packagequality.com/#?package=use-http\"\u003e\n    \u003cimg src=\"https://npm.packagequality.com/shield/use-http.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://standardjs.com\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/code_style-standard-brightgreen.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.youtube.com/playlist?list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/youtube-subscribe-RED.svg\" /\u003e\n  \u003c/a\u003e\n\n\u003c!-- [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/next-js) --\u003e\n\u003c!--     \u003ca href=\"https://bundlephobia.com/result?p=use-http\"\u003e\n      \u003cimg alt=\"undefined\" src=\"https://img.shields.io/bundlephobia/minzip/use-http.svg\"\u003e\n    \u003c/a\u003e --\u003e\n\u003c!--     \u003ca href=\"https://snyk.io/test/github/ava/use-http?targetFile=package.json\"\u003e\n      \u003cimg src=\"https://snyk.io/test/github/ava/use-http/badge.svg?targetFile=package.json\" alt=\"Known Vulnerabilities\" data-canonical-src=\"https://snyk.io/test/github/ava/use-http?targetFile=package.json\" style=\"max-width:100%;\"\u003e\n    \u003c/a\u003e --\u003e\n\u003c!--     \u003ca href=\"https://www.npmjs.com/package/use-http\"\u003e\n      \u003cimg src=\"https://img.shields.io/npm/v/use-http.svg\" alt=\"Known Vulnerabilities\" data-canonical-src=\"https://snyk.io/test/github/ava/use-http?targetFile=package.json\" style=\"max-width:100%;\"\u003e\n    \u003c/a\u003e --\u003e\n\u003c!--     \u003ca href=\"https://github.com/ava/use-http/blob/master/license.md\"\u003e\n      \u003cimg alt=\"undefined\" src=\"https://img.shields.io/github/license/ava/use-http.svg\"\u003e\n    \u003c/a\u003e --\u003e\n\u003c!--     \u003ca href=\"https://greenkeeper.io/\"\u003e\n      \u003cimg alt=\"undefined\" src=\"https://badges.greenkeeper.io/ava/use-http.svg\"\u003e\n    \u003c/a\u003e --\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003csup\u003e\n    🐶 React hook for making isomorphic http requests\n    \u003cbr/\u003e\n      \u003ca href=\"https://use-http.com\"\u003e\u003cb\u003eMain Documentation\u003c/b\u003e\u003c/a\u003e\n  \u003c/sup\u003e\n\u003c/div\u003e\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n    \n\u003cdiv align=\"center\"\u003e\n  \u003cpre\u003enpm i \u003ca href=\"https://use-http.com\"\u003euse-http\u003c/a\u003e\u003c/pre\u003e\n\u003c/div\u003e\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\nFeatures\n---------\n\n- SSR (server side rendering) support\n- TypeScript support\n- 2 dependencies ([use-ssr](https://github.com/alex-cory/use-ssr), [urs](https://github.com/alex-cory/urs))\n- GraphQL support (queries + mutations)\n- Provider to set default `url` and `options`\n- Request/response interceptors \u003c!--https://github.com/ava/use-http#user-content-interceptors--\u003e\n- React Native support\n- Aborts/Cancels pending http requests when a component unmounts\n- Built in caching\n- Persistent caching support\n- Suspense\u003csup\u003e(experimental)\u003c/sup\u003e support\n- Retry functionality\n\nUsage\n-----\n\n### Examples + Videos\n\n- useFetch - managed state, request, response, etc. [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-request-response-managed-state-ruyi3?file=/src/index.js) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=_-GujYZFCKI\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=6)\n- useFetch - request/response interceptors [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-provider-requestresponse-interceptors-s1lex) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=3HauoWh0Jts\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=8)\n- useFetch - retries, retryOn, retryDelay [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-retryon-retrydelay-s74q9) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=grE3AX-Q9ss\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=9)\n- useFetch - abort, timeout, onAbort, onTimeout [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=7SuD3ZOfu7E\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=4)\n- useFetch - persist, cache [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=pJ22Rq9c8mw\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=7)\n- useFetch - cacheLife, cachePolicy [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=AsZ9hnWHCeg\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=3\u0026t=0s)\n- useFetch - suspense \u003csup\u003e(experimental)\u003c/sup\u003e [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-suspense-i22wv) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=7qWLJUpnxHI\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=2\u0026t=0s)\n- useFetch - pagination [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-provider-pagination-exttg) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=YmcMjRpIYqU\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=5)\n- useQuery - GraphQL [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/graphql-usequery-provider-uhdmj)\n- useFetch - Next.js [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-in-nextjs-nn9fm)\n- useFetch - create-react-app [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/embed/km04k9k9x5)\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cb\u003eBasic Usage Managed State \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\nIf the last argument of `useFetch` is not a dependency array `[]`, then it will not fire until you call one of the http methods like `get`, `post`, etc.\n\n```js\nimport useFetch from 'use-http'\n\nfunction Todos() {\n  const [todos, setTodos] = useState([])\n  const { get, post, response, loading, error } = useFetch('https://example.com')\n\n  useEffect(() =\u003e { initializeTodos() }, []) // componentDidMount\n  \n  async function initializeTodos() {\n    const initialTodos = await get('/todos')\n    if (response.ok) setTodos(initialTodos)\n  }\n\n  async function addTodo() {\n    const newTodo = await post('/todos', { title: 'my new todo' })\n    if (response.ok) setTodos([...todos, newTodo])\n  }\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={addTodo}\u003eAdd Todo\u003c/button\u003e\n      {error \u0026\u0026 'Error!'}\n      {loading \u0026\u0026 'Loading...'}\n      {todos.map(todo =\u003e (\n        \u003cdiv key={todo.id}\u003e{todo.title}\u003c/div\u003e\n      ))}\n    \u003c/\u003e\n  )\n}\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-request-response-managed-state-ruyi3?file=/src/index.js'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=_-GujYZFCKI\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=6'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cb\u003eBasic Usage Auto-Managed State \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\nThis fetch is run `onMount/componentDidMount`. The last argument `[]` means it will run `onMount`. If you pass it a variable like `[someVariable]`, it will run `onMount` and again whenever `someVariable` changes values (aka `onUpdate`). If no method is specified, GET is the default.\n\n```js\nimport useFetch from 'use-http'\n\nfunction Todos() {\n  const options = {} // these options accept all native `fetch` options\n  // the last argument below [] means it will fire onMount (GET by default)\n  const { loading, error, data = [] } = useFetch('https://example.com/todos', options, [])\n  return (\n    \u003c\u003e\n      {error \u0026\u0026 'Error!'}\n      {loading \u0026\u0026 'Loading...'}\n      {data.map(todo =\u003e (\n        \u003cdiv key={todo.id}\u003e{todo.title}\u003c/div\u003e\n      ))}\n    \u003c/\u003e\n  )\n}\n```\n\n\u003c!-- TODO: codesandbox + youtube --\u003e\n\u003c!-- \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='XXXXXX'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e --\u003e\n\n\u003c/details\u003e\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cb\u003eSuspense Mode\u003csup\u003e(experimental)\u003c/sup\u003e Auto-Managed State\u003c/b\u003e\u003c/summary\u003e\n\nCan put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B).\n\n```js\nimport useFetch, { Provider } from 'use-http'\n\nfunction Todos() {\n  const { data: todos = [] } = useFetch('/todos', {\n    suspense: true // A. can put `suspense: true` here\n  }, []) // onMount\n\n  return todos.map(todo =\u003e \u003cdiv key={todo.id}\u003e{todo.title}\u003c/div\u003e)\n}\n\nfunction App() {\n  const options = {\n    suspense: true // B. can put `suspense: true` here too\n  }\n  return (\n    \u003cProvider url='https://example.com' options={options}\u003e\n      \u003cSuspense fallback='Loading...'\u003e\n        \u003cTodos /\u003e\n      \u003c/Suspense\u003e\n    \u003c/Provider\u003e\n  )\n}\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-suspense-i22wv'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=7qWLJUpnxHI\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=2\u0026t=0s'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eSuspense Mode\u003csup\u003e(experimental)\u003c/sup\u003e Managed State\u003c/b\u003e\u003c/summary\u003e\n\nCan put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). Suspense mode via managed state is very experimental.\n\n```js\nimport useFetch, { Provider } from 'use-http'\n\nfunction Todos() {\n  const [todos, setTodos] = useState([])\n  // A. can put `suspense: true` here\n  const { get, response } = useFetch({ suspense: true })\n\n  const loadInitialTodos = async () =\u003e {\n    const todos = await get('/todos')\n    if (response.ok) setTodos(todos)\n  }\n\n  // componentDidMount\n  useEffect(() =\u003e {\n    loadInitialTodos()\n  }, [])\n\n  return todos.map(todo =\u003e \u003cdiv key={todo.id}\u003e{todo.title}\u003c/div\u003e)\n}\n\nfunction App() {\n  const options = {\n    suspense: true // B. can put `suspense: true` here too\n  }\n  return (\n    \u003cProvider url='https://example.com' options={options}\u003e\n      \u003cSuspense fallback='Loading...'\u003e\n        \u003cTodos /\u003e\n      \u003c/Suspense\u003e\n    \u003c/Provider\u003e\n  )\n}\n```\n\n\u003c/details\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n  \u003chr\u003e\n  \u003cp\u003e\n    \u003csup\u003e\n      \u003ca href=\"https://github.com/sponsors/alex-cory\"\u003eConsider sponsoring\u003c/a\u003e\n    \u003c/sup\u003e\n    \u003cbr\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://ava.inc\"\u003e\n      \u003cimg src=\"public/ava-logo.png\" width=\"130\" alt=\"Ava\"\u003e\n    \u003c/a\u003e\n    \u003cbr\u003e\n    \u003csub\u003e\u003cb\u003eAva, Rapid Application Development\u003c/b\u003e\u003c/sub\u003e\n    \u003cbr\u003e\n    \u003csub\u003e\n    Need a freelance software engineer with more than 5 years production experience at companies like Facebook, Discord, Best Buy, and Citrix?\u003c/br\u003e\n    \u003ca href=\"https://ava.inc\"\u003ewebsite\u003c/a\u003e | \u003ca href=\"https://mail.google.com/mail/?view=cm\u0026fs=1\u0026tf=1\u0026to=alex@ava.inc\"\u003eemail\u003c/a\u003e | \u003ca href=\"https://twitter.com/@alexcory_\"\u003etwitter\u003c/a\u003e\n    \u003c/sub\u003e\n  \u003c/p\u003e\n  \u003chr\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/div\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003ePagination + \u003ccode\u003eProvider\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\nThe `onNewData` will take the current data, and the newly fetched data, and allow you to merge the two however you choose. In the example below, we are appending the new todos to the end of the current todos.\n\n```jsx\nimport useFetch, { Provider } from 'use-http'\n\nconst Todos = () =\u003e {\n  const [page, setPage] = useState(1)\n\n  const { data = [], loading } = useFetch(`/todos?page=${page}\u0026amountPerPage=15`, {\n    onNewData: (currTodos, newTodos) =\u003e [...currTodos, ...newTodos], // appends newly fetched todos\n    perPage: 15, // stops making more requests if last todos fetched \u003c 15\n  }, [page]) // runs onMount AND whenever the `page` updates (onUpdate)\n\n  return (\n    \u003cul\u003e\n      {data.map(todo =\u003e \u003cli key={todo.id}\u003e{todo.title}\u003c/li\u003e}\n      {loading \u0026\u0026 'Loading...'}\n      {!loading \u0026\u0026 (\n        \u003cbutton onClick={() =\u003e setPage(page + 1)}\u003eLoad More Todos\u003c/button\u003e\n      )}\n    \u003c/ul\u003e\n  )\n}\n\nconst App = () =\u003e (\n  \u003cProvider url='https://example.com'\u003e\n    \u003cTodos /\u003e\n  \u003c/Provider\u003e\n)\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-provider-pagination-exttg?file=/src/index.js'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=YmcMjRpIYqU\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=5'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\n\u003c/details\u003e\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cb\u003eDestructured \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\n⚠️ Do not destructure the `response` object! Details in [this video](https://youtu.be/_-GujYZFCKI?list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026t=127). Technically you can do it, but if you need to access the `response.ok` from, for example, within a component's onClick handler, it will be a stale value for `ok` where it will be correct for `response.ok`.  ️️⚠️\n\n```js\nvar [request, response, loading, error] = useFetch('https://example.com')\n\n// want to use object destructuring? You can do that too\nvar {\n  request,\n  response, // 🚨 Do not destructure the `response` object!\n  loading,\n  error,\n  data,\n  cache,    // methods: get, set, has, delete, clear (like `new Map()`)\n  get,\n  post,\n  put,\n  patch,\n  delete    // don't destructure `delete` though, it's a keyword\n  del,      // \u003c- that's why we have this (del). or use `request.delete`\n  head,\n  options,\n  connect,\n  trace,\n  mutate,   // GraphQL\n  query,    // GraphQL\n  abort\n} = useFetch('https://example.com')\n\n// 🚨 Do not destructure the `response` object!\n// 🚨 This just shows what fields are available in it.\nvar {\n  ok,\n  status,\n  headers,\n  data,\n  type,\n  statusText,\n  url,\n  body,\n  bodyUsed,\n  redirected,\n  // methods\n  json,\n  text,\n  formData,\n  blob,\n  arrayBuffer,\n  clone\n} = response\n\nvar {\n  loading,\n  error,\n  data,\n  cache,  // methods: get, set, has, delete, clear (like `new Map()`)\n  get,\n  post,\n  put,\n  patch,\n  delete  // don't destructure `delete` though, it's a keyword\n  del,    // \u003c- that's why we have this (del). or use `request.delete`\n  mutate, // GraphQL\n  query,  // GraphQL\n  abort\n} = request\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=_-GujYZFCKI\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=6'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eRelative routes \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\n```jsx\nvar request = useFetch('https://example.com')\n\nrequest.post('/todos', {\n  no: 'way'\n})\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=JWDL_AVOYT0\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=10'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eAbort \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\n\u003cimg src=\"public/abort-example-1.gif\" height=\"250\" /\u003e\n\n\n```jsx\nconst { get, abort, loading, data: repos } = useFetch('https://api.github.com/search/repositories?q=')\n\n// the line below is not isomorphic, but for simplicity we're using the browsers `encodeURI`\nconst searchGithubRepos = e =\u003e get(encodeURI(e.target.value))\n\n\u003c\u003e\n  \u003cinput onChange={searchGithubRepos} /\u003e\n  \u003cbutton onClick={abort}\u003eAbort\u003c/button\u003e\n  {loading ? 'Loading...' : repos.data.items.map(repo =\u003e (\n    \u003cdiv key={repo.id}\u003e{repo.name}\u003c/div\u003e\n  ))}\n\u003c/\u003e\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=7SuD3ZOfu7E\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=4'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGraphQL Query \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\n```jsx\n\nconst QUERY = `\n  query Todos($userID string!) {\n    todos(userID: $userID) {\n      id\n      title\n    }\n  }\n`\n\nfunction App() {\n  const request = useFetch('http://example.com')\n\n  const getTodosForUser = id =\u003e request.query(QUERY, { userID: id })\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={() =\u003e getTodosForUser('theUsersID')}\u003eGet User's Todos\u003c/button\u003e\n      {request.loading ? 'Loading...' : \u003cpre\u003e{request.data}\u003c/pre\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGraphQL Mutation \u003ccode\u003euseFetch\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\nThe `Provider` allows us to set a default `url`, `options` (such as headers) and so on.\n\n```jsx\n\nconst MUTATION = `\n  mutation CreateTodo($todoTitle string) {\n    todo(title: $todoTitle) {\n      id\n      title\n    }\n  }\n`\n\nfunction App() {\n  const [todoTitle, setTodoTitle] = useState('')\n  const request = useFetch('http://example.com')\n\n  const createtodo = () =\u003e request.mutate(MUTATION, { todoTitle })\n\n  return (\n    \u003c\u003e\n      \u003cinput onChange={e =\u003e setTodoTitle(e.target.value)} /\u003e\n      \u003cbutton onClick={createTodo}\u003eCreate Todo\u003c/button\u003e\n      {request.loading ? 'Loading...' : \u003cpre\u003e{request.data}\u003c/pre\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003e\u003ccode\u003eProvider\u003c/code\u003e using the GraphQL \u003ccode\u003euseMutation\u003c/code\u003e and \u003ccode\u003euseQuery\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n\n##### Query for todos\n```jsx\nimport { useQuery } from 'use-http'\n\nexport default function QueryComponent() {\n  \n  // can also do it this way:\n  // const [data, loading, error, query] = useQuery`\n  // or this way:\n  // const { data, loading, error, query } = useQuery`\n  const request = useQuery`\n    query Todos($userID string!) {\n      todos(userID: $userID) {\n        id\n        title\n      }\n    }\n  `\n\n  const getTodosForUser = id =\u003e request.query({ userID: id })\n  \n  return (\n    \u003c\u003e\n      \u003cbutton onClick={() =\u003e getTodosForUser('theUsersID')}\u003eGet User's Todos\u003c/button\u003e\n      {request.loading ? 'Loading...' : \u003cpre\u003e{request.data}\u003c/pre\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/graphql-usequery-provider-uhdmj'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e \n\n\n##### Add a new todo\n```jsx\nimport { useMutation } from 'use-http'\n\nexport default function MutationComponent() {\n  const [todoTitle, setTodoTitle] = useState('')\n  \n  // can also do it this way:\n  // const request = useMutation`\n  // or this way:\n  // const { data, loading, error, mutate } = useMutation`\n  const [data, loading, error, mutate] = useMutation`\n    mutation CreateTodo($todoTitle string) {\n      todo(title: $todoTitle) {\n        id\n        title\n      }\n    }\n  `\n  \n  const createTodo = () =\u003e mutate({ todoTitle })\n\n  return (\n    \u003c\u003e\n      \u003cinput onChange={e =\u003e setTodoTitle(e.target.value)} /\u003e\n      \u003cbutton onClick={createTodo}\u003eCreate Todo\u003c/button\u003e\n      {loading ? 'Loading...' : \u003cpre\u003e{data}\u003c/pre\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\n\n##### Adding the Provider\nThese props are defaults used in every request inside the `\u003cProvider /\u003e`. They can be overwritten individually\n```jsx\nimport { Provider } from 'use-http'\nimport QueryComponent from './QueryComponent'\nimport MutationComponent from './MutationComponent'\n\nfunction App() {\n\n  const options = {\n    headers: {\n      Authorization: 'Bearer YOUR_TOKEN_HERE'\n    }\n  }\n  \n  return (\n    \u003cProvider url='http://example.com' options={options}\u003e\n      \u003cQueryComponent /\u003e\n      \u003cMutationComponent /\u003e\n    \u003cProvider/\u003e\n  )\n}\n\n```\n\u003c/details\u003e\n\n\n\u003cdetails id='interceptors'\u003e\u003csummary\u003e\u003cb\u003eRequest/Response Interceptors\u003c/b\u003e\u003c/summary\u003e\n    \nThis example shows how we can do authentication in the `request` interceptor and how we can camelCase the results in the `response` interceptor\n    \n```jsx\nimport { Provider } from 'use-http'\nimport { toCamel } from 'convert-keys'\n\nfunction App() {\n  let [token, setToken] = useLocalStorage('token')\n  \n  const options = {\n    interceptors: {\n      // every time we make an http request, this will run 1st before the request is made\n      // url, path and route are supplied to the interceptor\n      // request options can be modified and must be returned\n      request: async ({ options, url, path, route }) =\u003e {\n        if (isExpired(token)) {\n          token = await getNewToken()\n          setToken(token)\n        }\n        options.headers.Authorization = `Bearer ${token}`\n        return options\n      },\n      // every time we make an http request, before getting the response back, this will run\n      response: async ({ response, request }) =\u003e {\n        // unfortunately, because this is a JS Response object, we have to modify it directly.\n        // It shouldn't have any negative affect since this is getting reset on each request.\n        const res = response\n        if (res.data) res.data = toCamel(res.data)\n        return res\n      }\n    }\n  }\n  \n  return (\n    \u003cProvider url='http://example.com' options={options}\u003e\n      \u003cSomeComponent /\u003e\n    \u003cProvider/\u003e\n  )\n}\n\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-provider-requestresponse-interceptors-s1lex'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=3HauoWh0Jts\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=8'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\n\u003cdetails id='form-data'\u003e\u003csummary\u003e\u003cb\u003eFile Uploads (FormData)\u003c/b\u003e\u003c/summary\u003e\n    \nThis example shows how we can upload a file using `useFetch`.\n    \n```jsx\nimport useFetch from 'use-http'\n\nconst FileUploader = () =\u003e {\n  const [file, setFile] = useState()\n  \n  const { post } = useFetch('https://example.com/upload')\n\n  const uploadFile = async () =\u003e {\n    const data = new FormData()\n    data.append('file', file)\n    if (file instanceof FormData) await post(data)\n  }\n\n  return (\n    \u003cdiv\u003e\n      {/* Drop a file onto the input below */}\n      \u003cinput onChange={e =\u003e setFile(e.target.files[0])} /\u003e\n      \u003cbutton onClick={uploadFile}\u003eUpload\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eHandling Different Response Types\u003c/b\u003e\u003c/summary\u003e\n    \nThis example shows how we can get `.json()`, `.text()`, `.formData()`, `.blob()`, `.arrayBuffer()`, and all the other [http response methods](https://developer.mozilla.org/en-US/docs/Web/API/Response#Methods). By default, `useFetch` 1st tries to call `response.json()` under the hood, if that fails it's backup is `response.text()`. If that fails, then you need a different response type which is where this comes in.\n\n```js\nimport useFetch from 'use-http'\n\nconst App = () =\u003e {\n  const [name, setName] = useState('')\n  \n  const { get, loading, error, response } = useFetch('http://example.com')\n\n  const handleClick = async () =\u003e {\n    await get('/users/1?name=true') // will return just the user's name\n    const text = await response.text()\n    setName(text)\n  }\n  \n  return (\n    \u003c\u003e\n      \u003cbutton onClick={handleClick}\u003eLoad Data\u003c/button\u003e\n      {error \u0026\u0026 error.messge}\n      {loading \u0026\u0026 \"Loading...\"}\n      {name \u0026\u0026 \u003cdiv\u003e{name}\u003c/div\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-different-response-types-c6csw'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e \n\u003c!-- \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='XXXXXX'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e --\u003e\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eOverwrite/Remove Options/Headers Set in Provider\u003c/b\u003e\u003c/summary\u003e\n    \nThis example shows how to remove a header all together. Let's say you have `\u003cProvider url='url.com' options={{ headers: { Authentication: 'Bearer MY_TOKEN' } }}\u003e\u003cApp /\u003e\u003c/Provider\u003e`, but for one api call, you don't want that header in your `useFetch` at all for one instance in your app. This would allow you to remove that.\n\n```js\nimport useFetch from 'use-http'\n\nconst Todos = () =\u003e {\n  // let's say for this request, you don't want the `Accept` header at all\n  const { loading, error, data: todos = [] } = useFetch('/todos', globalOptions =\u003e {\n    delete globalOptions.headers.Accept\n    return globalOptions\n  }, []) // onMount\n  return (\n    \u003c\u003e\n      {error \u0026\u0026 error.messge}\n      {loading \u0026\u0026 \"Loading...\"}\n      {todos \u0026\u0026 \u003cul\u003e{todos.map(todo =\u003e \u003cli key={todo.id}\u003e{todo.title}\u003c/li\u003e)}\u003c/ul\u003e}\n    \u003c/\u003e\n  )\n}\n\nconst App = () =\u003e {\n  const options = {\n    headers: {\n      Accept: 'application/json'\n    }\n  }\n  return (\n    \u003cProvider url='https://url.com' options={options}\u003e\u003cTodos /\u003e\u003c/Provider\u003e\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eRetries retryOn \u0026 retryDelay\u003c/b\u003e\u003c/summary\u003e\n\nIn this example you can see how `retryOn` will retry on a status code of `305`, or if we choose the `retryOn()` function, it returns a boolean to decide if we will retry. With `retryDelay` we can either have a fixed delay, or a dynamic one by using `retryDelay()`. Make sure `retries` is set to at minimum `1` otherwise it won't retry the request. If `retries \u003e 0` without `retryOn` then by default we always retry if there's an error or if `!response.ok`. If `retryOn: [400]` and `retries \u003e 0` then we only retry on a response status of `400`.\n\n```js\nimport useFetch from 'use-http'\n\nconst TestRetry = () =\u003e {\n  const { response, get } = useFetch('https://httpbin.org/status/305', {\n    // make sure `retries` is set otherwise it won't retry\n    retries: 1,\n    retryOn: [305],\n    // OR\n    retryOn: async ({ attempt, error, response }) =\u003e {\n      // returns true or false to determine whether to retry\n      return error || response \u0026\u0026 response.status \u003e= 300\n    },\n\n    retryDelay: 3000,\n    // OR\n    retryDelay: ({ attempt, error, response }) =\u003e {\n      // exponential backoff\n      return Math.min(attempt \u003e 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)\n      // linear backoff\n      return attempt * 1000\n    }\n  })\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={() =\u003e get()}\u003eCLICK\u003c/button\u003e\n      \u003cpre\u003e{JSON.stringify(response, null, 2)}\u003c/pre\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://codesandbox.io/s/usefetch-retryon-retrydelay-s74q9'\u003e\u003cimg  width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /\u003e\u003c/a\u003e  \u003ca target=\"_blank\" rel=\"noopener noreferrer\" href='https://www.youtube.com/watch?v=grE3AX-Q9ss\u0026list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW\u0026index=9'\u003e\u003cimg  width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /\u003e\u003c/a\u003e\n\n\u003c/details\u003e\n\nOverview\n--------\n\n### Hooks\n\n| Hook                | Description                                                                              |\n| --------------------- | ---------------------------------------------------------------------------------------- |\n| `useFetch` | The base hook |\n| `useQuery` | For making a GraphQL query |\n| `useMutation` | For making a GraphQL mutation |\n    \n\u003c/details\u003e\n\n\n### Options\n    \nThis is exactly what you would pass to the normal js `fetch`, with a little extra. All these options can be passed to the `\u003cProvider options={/* every option below */} /\u003e`, or directly to `useFetch`. If you have both in the `\u003cProvider /\u003e` and in `useFetch`, the `useFetch` options will overwrite the ones from the `\u003cProvider /\u003e`\n\n| Option                | Description                                                               |  Default     |\n| --------------------- | --------------------------------------------------------------------------|------------- |\n| `cacheLife` | After a successful cache update, that cache data will become stale after this duration       | `0` |\n| `cachePolicy` | These will be the same ones as Apollo's [fetch policies](https://www.apollographql.com/docs/react/api/react/hoc/#optionsfetchpolicy). Possible values are `cache-and-network`, `network-only`, `cache-only`, `no-cache`, `cache-first`. Currently only supports **`cache-first`**  or **`no-cache`**      | `cache-first` |\n| `data` | Allows you to set a default value for `data`       | `undefined` |\n| `interceptors.request` | Allows you to do something before an http request is sent out. Useful for authentication if you need to refresh tokens a lot.  | `undefined` |\n| `interceptors.response` | Allows you to do something after an http response is recieved. Useful for something like camelCasing the keys of the response.  | `undefined` |\n| `loading` | Allows you to set default value for `loading`       | `false` unless the last argument of `useFetch` is `[]` |\n| `onAbort` | Runs when the request is aborted. | empty function |\n| `onError` | Runs when the request get's an error. If retrying, it is only called on the last retry attempt. | empty function |\n| `onNewData` | Merges the current data with the incoming data. Great for pagination.  | `(curr, new) =\u003e new` |\n| `onTimeout` | Called when the request times out. | empty function |\n| `persist` | Persists data for the duration of `cacheLife`. If `cacheLife` is not set it defaults to 24h. Currently only available in Browser. | `false` |\n| `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` |\n| `responseType` | This will determine how the `data` field is set. If you put `json` then it will try to parse it as JSON. If you set it as an array, it will attempt to parse the `response` in the order of the types you put in the array. Read about why we don't put `formData` in the defaults [in the yellow Note part here](https://developer.mozilla.org/en-US/docs/Web/API/Body/formData).  | `['json', 'text', 'blob', 'readableStream']` |\n| `retries` | When a request fails or times out, retry the request this many times. By default it will not retry.    | `0` |\n| `retryDelay` | You can retry with certain intervals i.e. 30 seconds `30000` or with custom logic (i.e. to increase retry intervals). | `1000` |\n| `retryOn` | You can retry on certain http status codes or have custom logic to decide whether to retry or not via a function. Make sure `retries \u003e 0` otherwise it won't retry. | `[]` |\n| `suspense` | Enables Experimental React Suspense mode. [example](https://codesandbox.io/s/usefetch-suspense-i22wv) | `false` |\n| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds**. If set to `0`, it will not timeout except for browser defaults.       | `0` |\n\n```jsx\nconst options = {\n  // accepts all `fetch` options such as headers, method, etc.\n\n  // The time in milliseconds that cache data remains fresh.\n  cacheLife: 0,\n\n  // Cache responses to improve speed and reduce amount of requests\n  // Only one request to the same endpoint will be initiated unless cacheLife expires for 'cache-first'.\n  cachePolicy: 'cache-first', // 'no-cache'\n  \n  // set's the default for the `data` field\n  data: [],\n\n  // typically, `interceptors` would be added as an option to the `\u003cProvider /\u003e`\n  interceptors: {\n    request: async ({ options, url, path, route }) =\u003e { // `async` is not required\n      return options // returning the `options` is important\n    },\n    response: async ({ response, request }) =\u003e {\n      // notes:\n      // - `response.data` is equivalent to `await response.json()`\n      // - `request` is an object matching the standard fetch's options\n      return response // returning the `response` is important\n    }\n  },\n\n  // set's the default for `loading` field\n  loading: false,\n  \n  // called when aborting the request\n  onAbort: () =\u003e {},\n  \n  // runs when an error happens.\n  onError: ({ error }) =\u003e {},\n\n  // this will allow you to merge the `data` for pagination.\n  onNewData: (currData, newData) =\u003e {\n    return [...currData, ...newData] \n  },\n  \n  // called when the request times out\n  onTimeout: () =\u003e {},\n  \n  // this will tell useFetch not to run the request if the list doesn't haveMore. (pagination)\n  // i.e. if the last page fetched was \u003c 15, don't run the request again\n  perPage: 15,\n\n  // Allows caching to persist after page refresh. Only supported in the Browser currently.\n  persist: false,\n\n  // this would basically call `await response.json()`\n  // and set the `data` and `response.data` field to the output\n  responseType: 'json',\n  // OR can be an array. It's an array by default.\n  // We will try to get the `data` by attempting to extract\n  // it via these body interface methods, one by one in\n  // this order. We skip `formData` because it's mostly used\n  // for service workers.\n  responseType: ['json', 'text', 'blob', 'arrayBuffer'],\n\n  // amount of times it should retry before erroring out\n  retries: 3,\n\n  // The time between retries\n  retryDelay: 10000,\n  // OR\n  // Can be a function which is used if we want change the time in between each retry\n  retryDelay({ attempt, error, response }) {\n    // exponential backoff\n    return Math.min(attempt \u003e 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)\n    // linear backoff\n    return attempt * 1000\n  },\n\n  // make sure `retries` is set otherwise it won't retry\n  // can retry on certain http status codes\n  retryOn: [503],\n  // OR\n  async retryOn({ attempt, error, response }) {\n    // retry on any network error, or 4xx or 5xx status codes\n    if (error !== null || response.status \u003e= 400) {\n      console.log(`retrying, attempt number ${attempt + 1}`);\n      return true;\n    }\n  },\n\n  // enables experimental React Suspense mode\n  suspense: true, // defaults to `false`\n  \n  // amount of time before the request get's canceled/aborted\n  timeout: 10000,\n}\n\nuseFetch(options)\n// OR\n\u003cProvider options={options}\u003e\u003cResOfYourApp /\u003e\u003c/Provider\u003e\n```\n\nWho's using use-http?\n----------------------\n\nDoes your company use use-http? Consider sponsoring the project to fund new features, bug fixes, and more.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg height=\"140px\" src=\"https://user-images.githubusercontent.com/5455859/98412764-5c5abe00-202d-11eb-9a2d-73377cfbfd86.png\" /\u003e\n  \u003ca href=\"https://ava.inc\" style=\"margin-right: 2rem;\" target=\"_blank\"\u003e\n    \u003cimg width=\"110px\" src=\"https://ava.inc/ava-logo-green.png\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/microsoft/DLWorkspace\"\u003e\n    \u003cimg height=\"110px\" src=\"https://github.com/ava/use-http/raw/master/public/microsoft-logo.png\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/mozilla/Spoke\"\u003e\n    \u003cimg height=\"110px\" src=\"https://github.com/ava/use-http/raw/master/public/mozilla.png\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://beapte.com\"\u003e\n    \u003cimg height=\"140px\" src=\"https://github.com/ava/use-http/raw/master/public/apte-logo.png\" /\u003e\n  \u003c/a\u003e\n\n\u003c!--   \u003ca href=\"#\"\u003e\n    \u003cimg height=\"140px\" src=\"https://user-images.githubusercontent.com/5455859/98412608-1aca1300-202d-11eb-9d20-295ce85bda9c.png\" /\u003e\n  \u003c/a\u003e --\u003e\n\u003c/p\u003e\n\nBrowser Support\n---------------\n\nIf you need support for IE, you will need to add additional polyfills.  The React docs suggest [these polyfills][4], but from [this issue][2] we have found it to work fine with the [`react-app-polyfill`]. If you have any updates to this browser list, please submit a PR!\n\n| [\u003cimg src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png\" alt=\"IE / Edge\" width=\"24px\" height=\"24px\" /\u003e]()\u003cbr/\u003eEdge | [\u003cimg src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png\" alt=\"Firefox\" width=\"24px\" height=\"24px\" /\u003e]()\u003cbr/\u003eFirefox | [\u003cimg src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png\" alt=\"Chrome\" width=\"24px\" height=\"24px\" /\u003e]()\u003cbr/\u003eChrome | [\u003cimg src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png\" alt=\"Safari\" width=\"24px\" height=\"24px\" /\u003e]()\u003cbr/\u003eSafari | [\u003cimg src=\"https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png\" alt=\"Opera\" width=\"24px\" height=\"24px\" /\u003e]()\u003cbr/\u003eOpera |\n| --------- | --------- | --------- | --------- | --------- |\n| 12+ | last 2 versions| last 2 versions| last 2 versions| last 2 versions |\n\nFeature Requests/Ideas\n----------------------\n\nIf you have feature requests, [submit an issue][1] to let us know what you would like to see!\n\nTodos\n------\n- [ ] [OSS analytics](https://www.npmjs.com/package/@scarf/scarf)\n- [ ] [contributors](https://github.com/all-contributors/all-contributors)\n- [ ] prefetching\n- [ ] global cache state management\n- [ ] optimistic updates\n- [ ] `persist` support for React Native\n- [ ] better loading state management. When using only 1 useFetch in a component and we use\n      `Promise.all([get('/todos/1'), get('/todos/2')])` then don't have a loading true,\n      loading false on each request. Just have loading true on 1st request, and loading false\n      on last request.\n- [ ] is making a [gitpod](https://www.gitpod.io/docs/configuration/) useful here? 🤔\n- [ ] suspense\n  - [ ] triggering it from outside the `\u003cSuspense /\u003e` component.\n    - add `.read()` to `request`\n    - or make it work with just the `suspense: true` option\n    - both of these options need to be thought out a lot more^\n  - [ ] tests for this^ (triggering outside)\n  - [ ] cleanup tests in general. Snapshot tests are unpredictably not working for some reason.\n    - snapshot test resources: [swr](https://github.com/zeit/swr/blob/master/test/use-swr.test.tsx#L1083), [react-apollo-hooks](https://github.com/trojanowski/react-apollo-hooks/blob/master/src/__tests__/useQuery-test.tsx#L218)\n    - basic test resources: [fetch-suspense](https://github.com/CharlesStover/fetch-suspense/blob/master/tests/create-use-fetch.test.ts), [@testing-library/react-hooks suspense PR](https://github.com/testing-library/react-hooks-testing-library/pull/35/files)\n- [ ] maybe add translations [like this one](https://github.com/jamiebuilds/unstated-next)\n- [ ] maybe add contributors [all-contributors](https://github.com/all-contributors/all-contributors)\n- [ ] add sponsors [similar to this](https://github.com/carbon-app/carbon)\n- [ ] Error handling\n  - [ ] if calling `response.json()` and there is no response yet\n- [ ] tests\n  - [ ] tests for SSR\n  - [ ] tests for react native [see here](https://stackoverflow.com/questions/45842088/react-native-mocking-formdata-in-unit-tests)\n  - [ ] tests for GraphQL hooks `useMutation` + `useQuery`\n  - [ ] tests for stale `response` see this [PR](https://github.com/ava/use-http/pull/119/files)\n  - [ ] tests to make sure `response.formData()` and some of the other http `response methods` work properly\n  - [ ] the `onMount` works properly with all variants of passing `useEffect(fn, [request.get])` and not causing an infinite loop\n  - [ ] `async` tests for `interceptors.response`\n  - [ ] aborts fetch on unmount\n  - [ ] does not abort fetch on every rerender\n  - [ ] `retryDelay` and `timeout` are both set. It works, but is annoying to deal with timers in tests. [resource](https://github.com/fac-13/HP-game/issues/9)\n  - [ ] `timeout` with `retries \u003e 0`. (also do `retires \u003e 1`) Need to figure out how to advance timers properly to write this and the test above\n- [ ] take a look at how [react-apollo-hooks](https://github.com/trojanowski/react-apollo-hooks) work. Maybe ad `useSubscription` and `const request = useFetch(); request.subscribe()` or something along those lines\n- [ ] make this a github package\n  - [see ava packages](https://github.com/orgs/ava/packages)\n- [ ] Documentation:\n  - [ ] show comparison with Apollo\n  - [ ] figure out a good way to show side-by-side comparisons\n  - [ ] show comparison with Axios\n- [ ] potential option ideas\n\n  ```jsx\n  const request = useFetch({\n    graphql: {\n      // all options can also be put in here\n      // to overwrite those of `useFetch` for\n      // `useMutation` and `useQuery`\n    },\n    // by default this is true, but if set to false\n    // then we default to the responseType array of trying 'json' first, then 'text', etc.\n    // hopefully I get some answers on here: https://bit.ly/3afPlJS\n    responseTypeGuessing: true,\n\n    // Allows you to pass in your own cache to useFetch\n    // This is controversial though because `cache` is an option in the requestInit\n    // and it's value is a string. See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n    // One possible solution is to move the default `fetch`'s `cache` to `cachePolicy`.\n    // I don't really like this solution though.\n    // Another solution is to only allow the `cache` option with the `\u003cProvider cache={new Map()} /\u003e`\n    cache: new Map(),\n    // these will be the exact same ones as Apollo's\n    cachePolicy: 'cache-and-network', 'network-only', 'cache-only', 'no-cache' // 'cache-first'\n    // potential idea to fetch on server instead of just having `loading` state. Not sure if this is a good idea though\n    onServer: true,\n    onSuccess: (/* idk what to put here */) =\u003e {},\n    // if you would prefer to pass the query in the config\n    query: `some graphql query`\n    // if you would prefer to pass the mutation in the config\n    mutation: `some graphql mutation`\n    refreshWhenHidden: false,\n  })\n\n\n  // potential for causing a rerender after clearing cache if needed\n  request.cache.clear(true)\n  ```\n\n- [ ] potential option ideas for `GraphQL`\n\n  ```jsx\n  const request = useQuery({ onMount: true })`your graphql query`\n\n  const request = useFetch(...)\n  const userID = 'some-user-uuid'\n  const res = await request.query({ userID })`\n    query Todos($userID string!) {\n      todos(userID: $userID) {\n        id\n        title\n      }\n    }\n  `\n  ```\n\n- [ ] make code editor plugin/package/extension that adds GraphQL syntax highlighting for `useQuery` and `useMutation` 😊\n\n- [ ] add React Native test suite\n\n[1]: https://github.com/ava/use-http/issues/new?title=[Feature%20Request]%20YOUR_FEATURE_NAME\n[2]: https://github.com/ava/use-http/issues/93#issuecomment-600896722\n[3]: https://github.com/ava/use-http/raw/master/public/dog.png\n[4]: https://reactjs.org/docs/javascript-environment-requirements.html\n[5]: https://use-http.com\n[`react-app-polyfill`]: https://www.npmjs.com/package/react-app-polyfill\n","funding_links":["https://github.com/sponsors/alex-cory"],"categories":["TypeScript","Packages","目录"],"sub_categories":["\u003ca id=\"other\"\u003e其他\u003c/a\u003e"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fava%2Fuse-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fava%2Fuse-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fava%2Fuse-http/lists"}