{"id":21934247,"url":"https://github.com/marcellomontemagno/react-ufo","last_synced_at":"2025-04-19T22:13:08.433Z","repository":{"id":36440958,"uuid":"222941955","full_name":"marcellomontemagno/react-ufo","owner":"marcellomontemagno","description":"🛸  A simple React hook to help you with data fetching 🛸","archived":false,"fork":false,"pushed_at":"2023-03-03T10:13:01.000Z","size":1122,"stargazers_count":85,"open_issues_count":9,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-14T13:41:23.440Z","etag":null,"topics":["abort","axios","fetch","fetch-api","hooks","react","react-hooks","reactjs","suspense"],"latest_commit_sha":null,"homepage":"","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/marcellomontemagno.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-20T13:18:57.000Z","updated_at":"2025-01-02T12:26:18.000Z","dependencies_parsed_at":"2024-06-19T11:18:09.163Z","dependency_job_id":"71d280bf-0732-4817-9c0a-b9b33822ef7a","html_url":"https://github.com/marcellomontemagno/react-ufo","commit_stats":{"total_commits":39,"total_committers":2,"mean_commits":19.5,"dds":0.02564102564102566,"last_synced_commit":"0cc88269656eb992445eb918b165947335dff3bc"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcellomontemagno%2Freact-ufo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcellomontemagno%2Freact-ufo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcellomontemagno%2Freact-ufo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcellomontemagno%2Freact-ufo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcellomontemagno","download_url":"https://codeload.github.com/marcellomontemagno/react-ufo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249819981,"owners_count":21329887,"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":["abort","axios","fetch","fetch-api","hooks","react","react-hooks","reactjs","suspense"],"created_at":"2024-11-29T00:15:00.020Z","updated_at":"2025-04-19T22:13:08.415Z","avatar_url":"https://github.com/marcellomontemagno.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"[![Actions Status](https://github.com/marcellomontemagno/react-ufo/workflows/CI/badge.svg)](https://github.com/marcellomontemagno/react-ufo/actions)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"ufo\" src='ufo.png'/\u003e\n  \u003cbr/\u003e\n  UFO - Use fetch orderly\n  \u003cbr/\u003e\n  A simple React hook to help you with data fetching\n\u003c/p\u003e\n\n## Introduction\n\n[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and [Axios](https://www.npmjs.com/package/axios) help you fetching data but when you need to link the status of a request to your React state you are on your own.\n\nHandling the UI state related to a request can be repetitive and error-prone, especially if you have to\n \n- handle related requests within the same component\n- ignore requests results after your component is unmounted\n- abort requests in certain conditions\n- handle race conditions\n\nTaking advantage of React hooks `react-ufo` helps you deal with this complexity.\n\n## Installation\n\n`npm install --save react-ufo`\n\n`import {useFetcher} from \"react-ufo\"`\n\n## How to use\n\n### Basic usage\n\n`useFetcher` handles the state of a request for you and much more.\n\nThe minimal usage of `useFetcher` looks like the following:\n\n`const [callback, [loading, error, data]] = useFetcher(fetcher)`\n\nA `fetcher` function is a normal function that fetches some data and returns a promise.\n\nHere an example of `fetcher` function: \n\n```js\nconst getTodo = async (id) =\u003e {\n  const response = await fetch(\"https://jsonplaceholder.typicode.com/todos/\" + id);\n  return response.json();\n};\n```\n  \nWhen you want your request to start, all you need to do is to invoke `callback`, after doing so, `loading`, `error`, and `data` will be updated in accordance with the status of your request. \n\nAny argument you pass to `callback` will be passed to your `fetcher`.\n\n\u003e **Note:**\n\u003e Do not create a new `fetcher` function on every render, `useFetcher` will create a new `callback` anytime a new `fetcher` instance is received. In case your `fetcher` depends on props simply pass them to `callback` and your fetcher will receive them.\n\nHere a basic example showing how to use `useFetcher` in an event callback such as `onClick` [![Edit 1basicFetchInEventCallbackExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/1basicfetchineventcallbackexample-ocu87?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n### Fetching on mount/update\n\nBy default, before a request is started, `useFetcher` will return `loading=false`, `error=null`, `data=null`.\n\nSometimes you might want your initial request state to be different.\n\nOne example is if you plan to request your data on the component mount/update, in this case, you might want your initial request state to have `loading=true`.\n    \n`useFetcher` can receive a second argument indicating the initial state before your request starts.\n\nHere how you override the default `loading` state to be `true`\n\n`const [callback, [loading, error, data]] = useFetcher(fetcher, {loading:true})`\n\nNow if you want your request to start on mount all you need to do is\n\n```js\nuseEffect(()=\u003e{\n  callback()\n},[callback])\n```\n\nYou don't have to worry about passing `callback` as a dependency of `useEffect`, `callback` will only change if your `fetcher` changes.\n\n### Fetching on mount/update with props\n\nSometimes a `fetcher` might need some data in order to retrieve data, for example, the `getTodo` presented earlier needs an `id` argument.\n\nAssuming `id` is a prop of your component all you need to do is\n\n```js\nuseEffect(()=\u003e{\n  callback(id)\n},[id,callback])\n```\n\nthis ensures that your `fetcher` will be invoked on mount and anytime `id` updates, which is usually what you want. \n\nHere a basic example showing how to use `useFetcher` during mount/update [![Edit 2basicFetchOnMountAndUpdateExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/2basicfetchonmountandupdateexample-k7e1q?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n### Cascading fetches\n\nSometimes 2 requests depend on each other.\n\nLet's say that you fetched a `todo` object containing a `userId` field and you want to use `userId` to fetch a `user` object.\n\nHere how you can handle this use case with `useFetcher`\n\n```js\n\n...\n\nconst [fetchTodo, [loadingTodo, todoError, todo]] = useFetcher(todoFetcher, {loading:true})\nconst [fetchUser, [loadingUser, userError, user]] = useFetcher(userFetcher, {loading:true})\n\nuseEffect(()=\u003e{\n  fetchTodo(todoId).then((todo)=\u003e{\n    fetchUser(todo.userId)\n  })\n},[todoId, fetchTodo, fetchUser])\n\n...\n\n```\n\nHere the full example showing this use case [![Edit 4cascadingFetchesExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/4cascadingfetchesexample-1148s?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n \n### Ignoring a pending request\n\nIf your component is unmounted while one of its requests is still pending `useFetcher` will take care of ignoring its result avoiding an attempt to perform a `setState` on an unmounted component.\n\nSometimes you might want to ignore the result of a request for other reasons too.\n\n`callback.ignore()` can be invoked if you need to ignore the result of a pending request.\n\nIf a pending request is marked as ignored `loading`, `error` and `data` will not be updated once the request is completed.\n \n### Aborting a pending request\n\n`callback.abort()` can be invoked anytime you want to abort a pending request.\n\nUnfortunately in order for `callback.abort()` to work properly there is some little more wiring that you'll need to do.\n\n`useFetcher` will take care of passing an [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to your `fetcher` as its last argument.\n\nIn order for `callback.abort()` to work you'll need to pass the abort signal to your [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).\n\nHere an example showing how to enable fetch abortion on the `getTodo` `fetcher` presented earlier\n\n```js\nconst getTodo = async (id, signal) =\u003e {\n  const response = await fetch(\"https://jsonplaceholder.typicode.com/todos/\" + id, {signal});\n  return response.json();\n};\n```\n\nIf your fetcher is not passing the `abort signal` to `fetch API` invoking `callback.abort()` will not abort the request but the request will still be marked as ignored.\n \nIf a request is marked as ignored `loading`, `error` and `data` will not be updated once the request is completed. \n \nHere an example showing how to abort a request [![Edit 3basicAbortFetchExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/3basicabortfetchexample-kf591?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\nAborting a pending request is quite easy when using [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) but it can also be achieved if you are using other libraries such as [axios](https://www.npmjs.com/package/axios)\n\nIf you are wondering how to abort a request started by [Axios](https://www.npmjs.com/package/axios) instead of [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) you can find an example here [![Edit abortRequestIfUsingAxiosExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/abortrequestifusingaxiosexample-fg8de?fontsize=14\u0026hidenavigation=1\u0026theme=dark) \n\n### Keeping state between fetches\n\nBy default `useFetcher` erases the `data` of a request anytime a new one is started.\n\nMost of the times this is what you want but there are cases where you want to keep the `data` visible to the user until new `data` are retrieved.  \n\nIf you need to keep `data` between fetches you can simply use `useState` from React.\n\nHere an example showing how to keep `data` while multiple request are pending:\n\n```js\nconst [data, setData] = useState()\nconst [callback, [loading, error, _data]] = useFetcher(fetcher)\n\n...\n\nconst myEventCallback = ()=\u003e{\n  callback(1).then((data)=\u003e{\n    setData(data)\n    callback(2).then((data)=\u003e{\n        setData(data)\n    })\n  })\n} \n\n``` \n\nIn the previous example `_data` is set to null anytime a new request is started while `data` is only valued when a request is completed.\n\n### Debouncing requests\n\nHere an example showing one simple way to debounce requests [![Edit 5debounceFetchExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/5debouncefetchexample-mo18d?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n### Mutating state\n\nSometimes you might want to change your request state manually.\n\nOne common scenario when this can happen is if your user decides to ignore and remove a request error message displayed on the screen.\n\n`useFetcher` provides you `setLoading`, `setError`, `setData` and `setRequestState` for you to handle these use cases.\n\nHere the full signature of `useFetcher`:\n\n```js\nconst [callback, [loading, error, data], setRequestState] = useFetcher(fetcher)\nconst [setLoading, setError, setData] = setRequestState\n```\n\n`setLoading`, `setError`, `setData` and `setRequestState` should be self explanatory, they work exactly like the setState in `const [state, setState] = useState()`\n\n### Putting all together\n\nHere an example showing how `useFetcher` can be used to implement a simple CRUD application [![Edit 6crudExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/6crudexample-ggp4o?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n### useFetcher API\n\nHere the full `useFetcher` API\n\n```js\nconst initialRequestState = {loading:false, error:null, data:null} //these are the default values if initialRequestState is not provided\nconst [callback, requestState, setRequestState] = useFetcher(fetcher, initialRequestState)\nconst [loading, error, data] = requestState\nconst [setLoading, setError, setData] = setRequestState\n```\n \n### What exactly is useFetcher returning?\n\n`useFetcher` returns a `result` object shaped as follow:\n\n```js\n{\n  callback,\n  requestState: {\n    loading,\n    error,\n    data\n  },\n  setRequestState: {\n    setLoading,\n    setError,\n    setData\n  }\n}\n``` \n\n`result`, `requestState` and `setRequestState` are also iterable, therefore, if you find it convenient for renaming, you can destructure them into an array as follow\n\n```js\nconst [callback, [loading, error, data], [setLoading, setError, setData]] = result\n```\n\nWhen destructuring into an array you obviously need to rely on the order we specified for each key, therefore, in case you don't want to extract all the fields from `result`, you might need to write something like the following:\n\n```js\nconst [callback, [loading, , data], [, setError]] = result\n```\n\nBecause `result` is an object, accessing its fields by key (e.g `const data = result.requestState.data`) is going to work as expected too.\n\nBecause `result` is an object, doing object destructuring is going to work as expected too. \n\nNote that even though `setRequestState` contains `setLoading`, `setError`, `setData` it is a function and can be used to update `loading`, `error` and `data` in a single render.\n\n\u003e **Note:**\n\u003e Even though `result`, `requestState` and `setRequestState` are iterable they are not arrays, therefore something like `result[0]` or `result.requestState[0]` is not going to work.   \n\n## Examples\n\n1) Basic fetch in event callback [![Edit 1basicFetchInEventCallbackExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/1basicfetchineventcallbackexample-ocu87?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n2) Basic fetch on mount/update [![Edit 2basicFetchOnMountAndUpdateExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/2basicfetchonmountandupdateexample-k7e1q?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n3) Aborting a pending request [![Edit 3basicAbortFetchExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/3basicabortfetchexample-kf591?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n4) Handling requests depending on each others [![Edit 4cascadingFetchesExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/4cascadingfetchesexample-1148s?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n5) Debouncing requests [![Edit 5debounceFetchExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/5debouncefetchexample-mo18d?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n6) Simple CRUD application [![Edit 6crudExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/6crudexample-ggp4o?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n7) Aborting a pending request started with [axios](https://www.npmjs.com/package/axios) [![Edit abortRequestIfUsingAxiosExample](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/abortrequestifusingaxiosexample-fg8de?fontsize=14\u0026hidenavigation=1\u0026theme=dark)\n\n## Package versioning\n\nBreaking changes might be made between 0.x.x versions.\nStarting from version 1.0.0 every breaking change will result in a major version update.\nThe [changelog](https://github.com/marcellomontemagno/react-ufo/releases) will give you details about every change between versions.\n  \n## Dependencies\n\nThis package has zero dependencies but in order to support fetches abortion you will need \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/AbortController\" target=\"_blank\"\u003eAbortController\u003c/a\u003e (or a polyfill such as \u003ca href=\"https://www.npmjs.com/package/abortcontroller-polyfill\" target=\"_blank\"\u003eabortcontroller-polyfill\u003c/a\u003e) in your environment\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcellomontemagno%2Freact-ufo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcellomontemagno%2Freact-ufo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcellomontemagno%2Freact-ufo/lists"}