This 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.
1import useFetch from 'use-http'
23function Todos() {
4 const options = {} // these options accept all native `fetch` options
5 // the last argument below [] means it will fire onMount (GET by default)
6 const { loading, error, data = [] } = useFetch('https://example.com/todos', options, [])
7 return (
8 <>
9 {error && 'Error!'}
10 {loading && 'Loading...'}
11 {data.map(todo => (
12 <div key={todo.id}>{todo.title}</div>
13 ))}
14 </>
15 )
16}
Suspense Mode(experimental) Auto-Managed State
Can put suspense in 2 places. Either useFetch (A) or Provider (B).
1import useFetch, { Provider } from'use-http'23functionTodos() {
4const { data: todos = [] } = useFetch('/todos', {
5suspense: true// A. can put `suspense: true` here6 }, []) // onMount78return todos.map(todo =><divkey={todo.id}>{todo.title}</div>)
9}
1011functionApp() {
12const options = {
13suspense: true// B. can put `suspense: true` here too14 }
15return (
16<Providerurl='https://example.com'options={options}>17<Suspensefallback='Loading...'>18<Todos />19</Suspense>20</Provider>21 )
22}
Suspense Mode(experimental) Managed State
Can put suspense in 2 places. Either useFetch (A) or Provider (B). Suspense mode via managed state is very experimental.
Ava, Rapid Application Development
Need a freelance software engineer with more than 5 years production experience at companies like Facebook, Discord, Best Buy, and Citrix? website | email | twitter
Pagination + Provider
The 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.
1import useFetch, { Provider } from'use-http'23const Todos = () => {
4const [page, setPage] = useState(1)
56const { data = [], loading } = useFetch(`/todos?page=${page}&amountPerPage=15`, {
7onNewData: (currTodos, newTodos) => [...currTodos, ...newTodos], // appends newly fetched todos8perPage: 15, // stops making more requests if last todos fetched < 159 }, [page]) // runs onMount AND whenever the `page` updates (onUpdate)1011return (
12<ul>13 {data.map(todo => <likey={todo.id}>{todo.title}</li>}
14 {loading && 'Loading...'}
15 {!loading && (
16<buttononClick={() => setPage(page + 1)}>Load More Todos</button>17 )}
18</ul>19 )
20}
2122const App = () => (
23<Providerurl='https://example.com'>24<Todos />25</Provider>26)
Destructured useFetch
⚠️ Do not destructure the response object! Details in this video. 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. ️️⚠️
1var [request, response, loading, error] = useFetch('https://example.com')
23// want to use object destructuring? You can do that too
4var {
5 request,6 response, // 🚨 Do not destructure the `response` object!
7 loading,8 error,9 data,10 cache, // methods: get, set, has, delete, clear (like `new Map()`)
11 get,12 post,13 put,14 patch,15 delete // don't destructure `delete` though, it's a keyword
16 del, // <- that's why we have this (del). or use `request.delete`
17 head,18 options,19 connect,20 trace,21 mutate, // GraphQL
22 query, // GraphQL
23 abort
24} = useFetch('https://example.com')
2526// 🚨 Do not destructure the `response` object!
27// 🚨 This just shows what fields are available in it.
28var {
29 ok,30 status,31 headers,32 data,33 type,34 statusText,35 url,36 body,37 bodyUsed,38 redirected,39 // methods
40 json,41 text,42 formData,43 blob,44 arrayBuffer,45 clone
46} = response
4748var {
49 loading,50 error,51 data,52 cache, // methods: get, set, has, delete, clear (like `new Map()`)
53 get,54 post,55 put,56 patch,57 delete // don't destructure `delete` though, it's a keyword
58 del, // <- that's why we have this (del). or use `request.delete`
59 mutate, // GraphQL
60 query, // GraphQL
61 abort
62} = request
1const { get, abort, loading, data: repos } = useFetch('https://api.github.com/search/repositories?q=')
23// the line below is not isomorphic, but for simplicity we're using the browsers `encodeURI`4const searchGithubRepos = e => get(encodeURI(e.target.value))
56<>
7<inputonChange={searchGithubRepos} />8<buttononClick={abort}>Abort</button>9 {loading ? 'Loading...' : repos.data.items.map(repo => (
10<divkey={repo.id}>{repo.name}</div>11 ))}
12</>
This example shows how we can do authentication in the request interceptor and how we can camelCase the results in the response interceptor
1import { Provider } from'use-http'2import { toCamel } from'convert-keys'34functionApp() {
5let [token, setToken] = useLocalStorage('token')
67const options = {
8interceptors: {
9// every time we make an http request, this will run 1st before the request is made10// url, path and route are supplied to the interceptor11// request options can be modified and must be returned12request: async ({ options, url, path, route }) => {
13if (isExpired(token)) {
14 token = await getNewToken()
15 setToken(token)
16 }
17 options.headers.Authorization = `Bearer ${token}`18return options
19 },
20// every time we make an http request, before getting the response back, this will run21response: async ({ response, request }) => {
22// unfortunately, because this is a JS Response object, we have to modify it directly.23// It shouldn't have any negative affect since this is getting reset on each request.24const res = response
25if (res.data) res.data = toCamel(res.data)
26return res
27 }
28 }
29 }
3031return (
32<Providerurl='http://example.com'options={options}>33<SomeComponent />34<Provider/>35 )
36}
37
File Uploads (FormData)
This example shows how we can upload a file using useFetch.
This example shows how we can get .json(), .text(), .formData(), .blob(), .arrayBuffer(), and all the other http 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.
This example shows how to remove a header all together. Let's say you have <Provider url='url.com' options={{ headers: { Authentication: 'Bearer MY_TOKEN' } }}><App /></Provider>, 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.
In 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 > 0 without retryOn then by default we always retry if there's an error or if !response.ok. If retryOn: [400] and retries > 0 then we only retry on a response status of 400.
This is exactly what you would pass to the normal js fetch, with a little extra. All these options can be passed to the <Provider options={/* every option below */} />, or directly to useFetch. If you have both in the <Provider /> and in useFetch, the useFetch options will overwrite the ones from the <Provider />
Option
Description
Default
cacheLife
After a successful cache update, that cache data will become stale after this duration
0
cachePolicy
These will be the same ones as Apollo's fetch policies. Possible values are cache-and-network, network-only, cache-only, no-cache, cache-first. Currently only supports cache-first or no-cache
cache-first
data
Allows you to set a default value for data
undefined
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
interceptors.response
Allows you to do something after an http response is recieved. Useful for something like camelCasing the keys of the response.
undefined
loading
Allows you to set default value for loading
false unless the last argument of useFetch is []
onAbort
Runs when the request is aborted.
empty function
onError
Runs when the request get's an error. If retrying, it is only called on the last retry attempt.
empty function
onNewData
Merges the current data with the incoming data. Great for pagination.
(curr, new) => new
onTimeout
Called when the request times out.
empty function
persist
Persists data for the duration of cacheLife. If cacheLife is not set it defaults to 24h. Currently only available in Browser.
false
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
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.
['json', 'text', 'blob', 'readableStream']
retries
When a request fails or times out, retry the request this many times. By default it will not retry.
0
retryDelay
You can retry with certain intervals i.e. 30 seconds 30000 or with custom logic (i.e. to increase retry intervals).
1000
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 > 0 otherwise it won't retry.
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
1const options = {
2// accepts all `fetch` options such as headers, method, etc.34// The time in milliseconds that cache data remains fresh.5cacheLife: 0,
67// Cache responses to improve speed and reduce amount of requests8// Only one request to the same endpoint will be initiated unless cacheLife expires for 'cache-first'.9cachePolicy: 'cache-first', // 'no-cache'1011// set's the default for the `data` field12data: [],
1314// typically, `interceptors` would be added as an option to the `<Provider />`15interceptors: {
16request: async ({ options, url, path, route }) => { // `async` is not required17return options // returning the `options` is important18 },
19response: async ({ response, request }) => {
20// notes:21// - `response.data` is equivalent to `await response.json()`22// - `request` is an object matching the standard fetch's options23return response // returning the `response` is important24 }
25 },
2627// set's the default for `loading` field28loading: false,
2930// called when aborting the request31onAbort: () => {},
3233// runs when an error happens.34onError: ({ error }) => {},
3536// this will allow you to merge the `data` for pagination.37onNewData: (currData, newData) => {
38return [...currData, ...newData]
39 },
4041// called when the request times out42onTimeout: () => {},
4344// this will tell useFetch not to run the request if the list doesn't haveMore. (pagination)45// i.e. if the last page fetched was < 15, don't run the request again46perPage: 15,
4748// Allows caching to persist after page refresh. Only supported in the Browser currently.49persist: false,
5051// this would basically call `await response.json()`52// and set the `data` and `response.data` field to the output53responseType: 'json',
54// OR can be an array. It's an array by default.55// We will try to get the `data` by attempting to extract56// it via these body interface methods, one by one in57// this order. We skip `formData` because it's mostly used58// for service workers.59responseType: ['json', 'text', 'blob', 'arrayBuffer'],
6061// amount of times it should retry before erroring out62retries: 3,
6364// The time between retries65retryDelay: 10000,
66// OR67// Can be a function which is used if we want change the time in between each retry68retryDelay({ attempt, error, response }) {
69// exponential backoff70returnMath.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)
71// linear backoff72return attempt * 100073 },
7475// make sure `retries` is set otherwise it won't retry76// can retry on certain http status codes77retryOn: [503],
78// OR79asyncretryOn({ attempt, error, response }) {
80// retry on any network error, or 4xx or 5xx status codes81if (error !== null || response.status >= 400) {
82console.log(`retrying, attempt number ${attempt + 1}`);
83returntrue;
84 }
85 },
8687// enables experimental React Suspense mode88suspense: true, // defaults to `false`8990// amount of time before the request get's canceled/aborted91timeout: 10000,
92}
9394useFetch(options)
95// OR96<Provider options={options}><ResOfYourApp /></Provider>
Who's using use-http?
Does your company use use-http? Consider sponsoring the project to fund new features, bug fixes, and more.
Browser Support
If you need support for IE, you will need to add additional polyfills. The React docs suggest these polyfills, but from this issue 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!
Edge
Firefox
Chrome
Safari
Opera
12+
last 2 versions
last 2 versions
last 2 versions
last 2 versions
Feature Requests/Ideas
If you have feature requests, submit an issue to let us know what you would like to see!
better loading state management. When using only 1 useFetch in a component and we use
Promise.all([get('/todos/1'), get('/todos/2')]) then don't have a loading true,
loading false on each request. Just have loading true on 1st request, and loading false
on last request.
tests to make sure response.formData() and some of the other http response methods work properly
the onMount works properly with all variants of passing useEffect(fn, [request.get]) and not causing an infinite loop
async tests for interceptors.response
aborts fetch on unmount
does not abort fetch on every rerender
retryDelay and timeout are both set. It works, but is annoying to deal with timers in tests. resource
timeout with retries > 0. (also do retires > 1) Need to figure out how to advance timers properly to write this and the test above
take a look at how react-apollo-hooks work. Maybe ad useSubscription and const request = useFetch(); request.subscribe() or something along those lines
figure out a good way to show side-by-side comparisons
show comparison with Axios
potential option ideas
1const request = useFetch({
2graphql: {
3//alloptionscanalsobeputinhere4//tooverwritethoseof `useFetch` for5// `useMutation` and `useQuery`
6 },
7//bydefaultthisistrue, butifsettofalse8//thenwedefaulttotheresponseTypearrayoftrying 'json' first, then 'text', etc.
9//hopefully I getsomeanswersonhere: https://bit.ly/3afPlJS
10responseTypeGuessing: true,
1112// Allows youtopassinyourowncachetouseFetch13// This iscontroversialthoughbecause `cache` isanoptionintherequestInit14//andit'svalueisastring. See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache15// One possiblesolutionistomovethedefault `fetch`'s `cache` to `cachePolicy`.
16// I don'treallylikethissolutionthough.
17// Another solutionistoonlyallowthe `cache` optionwiththe `<Provider cache={new Map()} />`
18 cache: newMap(),
19// these will be the exact same ones as Apollo's20 cachePolicy: 'cache-and-network', 'network-only', 'cache-only', 'no-cache' // 'cache-first'21// potential idea to fetch on server instead of just having `loading` state. Not sure if this is a good idea though22 onServer: true,
23 onSuccess: (/* idkwhattoputhere */) => {},
24// if you would prefer to pass the query in the config25 query: `some graphql query`
26// if you would prefer to pass the mutation in the config27 mutation: `some graphql mutation`
28 refreshWhenHidden: false,
29})
303132// potential for causing a rerender after clearing cache if needed33request.cache.clear(true)
make code editor plugin/package/extension that adds GraphQL syntax highlighting for useQuery and useMutation 😊
add React Native test suite
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Info: project has a license file: license.md:0
Info: FSF or OSI recognized license: MIT License: license.md:0
Reason
Found 12/30 approved changesets -- score normalized to 4
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Warn: no security policy file detected
Warn: no security file to analyze
Warn: no security file to analyze
Warn: no security file to analyze
Reason
project is not fuzzed
Details
Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Warn: 0 commits out of 12 are checked with a SAST tool
Reason
32 existing vulnerabilities detected
Details
Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw
Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
Warn: Project is vulnerable to: GHSA-7gc6-qh9x-w6h8
Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq
Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj
Warn: Project is vulnerable to: GHSA-896r-f27r-55mw
Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw
Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
Warn: Project is vulnerable to: GHSA-7wpw-2hjm-89gp
Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
Warn: Project is vulnerable to: GHSA-w7rc-rwvf-8q5r
Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g
Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p
Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp
Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6
Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v
Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3
Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693
Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
Score
2.3
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.