![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
npm i use-http
url
and options
useFetch
If 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.
import useFetch from 'use-http'
function Todos() {
const [todos, setTodos] = useState([])
const [request, response] = useFetch('https://example.com')
// componentDidMount
const mounted = useRef(false)
useEffect(() => {
if (mounted.current) return
mounted.current= true
initializeTodos()
})
async function initializeTodos() {
const initialTodos = await request.get('/todos')
if (response.ok) setTodos(initialTodos)
}
async function addTodo() {
const newTodo = await request.post('/todos', {
title: 'no way',
})
if (response.ok) setTodos([...todos, newTodo])
}
return (
<>
<button onClick={addTodo}>Add Todo</button>
{request.error && 'Error!'}
{request.loading && 'Loading...'}
{todos.map(todo => (
<div key={todo.id}>{todo.title}</div>
)}
</>
)
}
useFetch
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
import useFetch from 'use-http'
function Todos() {
// accepts all `fetch` options
const options = {
data: [], // setting default for `data` as array instead of undefined
}
const { loading, error, data } = useFetch('https://example.com/todos', options, []) // onMount (GET by default)
return (
<>
{error && 'Error!'}
{loading && 'Loading...'}
{data.map(todo => (
<div key={todo.id}>{todo.title}</div>
)}
</>
)
}
Provider
import useFetch, { Provider } from 'use-http'
function Todos() {
const { loading, error, data } = useFetch({
path: '/todos',
data: []
}, []) // onMount
return (
<>
{error && 'Error!'}
{loading && 'Loading...'}
{data.map(todo => (
<div key={todo.id}>{todo.title}</div>
)}
</>
)
}
const App = () => (
<Provider url='https://example.com'>
<Todos />
</Provider>
)
import useFetch, { Provider } from 'use-http'
function Todos() {
const { data: todos } = useFetch({
path: '/todos',
data: [],
suspense: true // can put it in 2 places. Here or in Provider
}, []) // onMount
return todos.map(todo => <div key={todo.id}>{todo.title}</div>)
}
function App() {
const options = {
suspense: true
}
return (
<Provider url='https://example.com' options={options}>
<Suspense fallback='Loading...'>
<Todos />
</Suspense>
</Provider>
)
}
Can put suspense
in 2 places. Either useFetch
(A) or Provider
(B).
import useFetch, { Provider } from 'use-http'
function Todos() {
const [todos, setTodos] = useState([])
// A. can put `suspense: true` here
const { get, response } = useFetch({ data: [], suspense: true })
const loadInitialTodos = async () => {
const todos = await get('/todos')
if (response.ok) setTodos(todos)
}
const mounted = useRef(false)
useEffect(() => {
if (mounted.current) return
mounted.current = true
loadInitialTodos()
}, [])
return todos.map(todo => <div key={todo.id}>{todo.title}</div>)
}
function App() {
const options = {
suspense: true // B. can put `suspense: true` here too
}
return (
<Provider url='https://example.com' options={options}>
<Suspense fallback='Loading...'>
<Todos />
</Suspense>
</Provider>
)
}
Consider sponsoring
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
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.
import useFetch, { Provider } from 'use-http'
const Todos = () => {
const [page, setPage] = useState(1)
const { data, loading } = useFetch({
path: `/todos?page=${page}&amountPerPage=15`,
onNewData: (currTodos, newTodos) => [...currTodos, ...newTodos], // appends newly fetched todos
perPage: 15, // stops making more requests if last todos fetched < 15
data: []
}, [page]) // runs onMount AND whenever the `page` updates (onUpdate)
return (
<ul>
{data.map(todo => <li key={todo.id}>{todo.title}</li>}
{loading && 'Loading...'}
{!loading && (
<button onClick={() => setPage(page + 1)}>Load More Todos</button>
)}
</ul>
)
}
const App = () => (
<Provider url='https://example.com'>
<Todos />
</Provider>
)
useFetch
â ď¸ Do not destructure the response
object! 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
. ď¸ď¸â ď¸
var [request, response, loading, error] = useFetch('https://example.com')
// want to use object destructuring? You can do that too
var {
request,
response, // đ¨ Do not destructure the `response` object!
loading,
error,
data,
cache, // methods: get, set, has, delete, clear (like `new Map()`)
get,
post,
put,
patch,
delete // don't destructure `delete` though, it's a keyword
del, // <- that's why we have this (del). or use `request.delete`
mutate, // GraphQL
query, // GraphQL
abort
} = useFetch('https://example.com')
// đ¨ Do not destructure the `response` object!
// đ¨ This just shows what fields are available in it.
var {
ok,
status,
headers,
data,
type,
statusText,
url,
body,
bodyUsed,
redirected,
// methods
json,
text,
formData,
blob,
arrayBuffer,
clone
} = response
var {
loading,
error,
data,
cache, // methods: get, set, has, delete, clear (like `new Map()`)
get,
post,
put,
patch,
delete // don't destructure `delete` though, it's a keyword
del, // <- that's why we have this (del). or use `request.delete`
mutate, // GraphQL
query, // GraphQL
abort
} = request
useFetch
â ď¸ baseUrl
is no longer supported, it is now only url
var request = useFetch({ url: 'https://example.com' })
// OR
var request = useFetch('https://example.com')
request.post('/todos', {
no: 'way'
})
useFetch
const githubRepos = useFetch({
url: `https://api.github.com/search/repositories?q=`
})
// the line below is not isomorphic, but for simplicity we're using the browsers `encodeURI`
const searchGithubRepos = e => githubRepos.get(encodeURI(e.target.value))
<>
<input onChange={searchGithubRepos} />
<button onClick={githubRepos.abort}>Abort</button>
{githubRepos.loading ? 'Loading...' : githubRepos.data.items.map(repo => (
<div key={repo.id}>{repo.name}</div>
))}
</>
useFetch
const QUERY = `
query Todos($userID string!) {
todos(userID: $userID) {
id
title
}
}
`
function App() {
const request = useFetch('http://example.com')
const getTodosForUser = id => request.query(QUERY, { userID: id })
return (
<>
<button onClick={() => getTodosForUser('theUsersID')}>Get User's Todos</button>
{request.loading ? 'Loading...' : <pre>{request.data}</pre>}
</>
)
}
useFetch
The Provider
allows us to set a default url
, options
(such as headers) and so on.
const MUTATION = `
mutation CreateTodo($todoTitle string) {
todo(title: $todoTitle) {
id
title
}
}
`
function App() {
const [todoTitle, setTodoTitle] = useState('')
const request = useFetch('http://example.com')
const createtodo = () => request.mutate(MUTATION, { todoTitle })
return (
<>
<input onChange={e => setTodoTitle(e.target.value)} />
<button onClick={createTodo}>Create Todo</button>
{request.loading ? 'Loading...' : <pre>{request.data}</pre>}
</>
)
}
Provider
using the GraphQL useMutation
and useQuery
import { useQuery } from 'use-http'
export default function QueryComponent() {
// can also do it this way:
// const [data, loading, error, query] = useQuery`
// or this way:
// const { data, loading, error, query } = useQuery`
const request = useQuery`
query Todos($userID string!) {
todos(userID: $userID) {
id
title
}
}
`
const getTodosForUser = id => request.query({ userID: id })
return (
<>
<button onClick={() => getTodosForUser('theUsersID')}>Get User's Todos</button>
{request.loading ? 'Loading...' : <pre>{request.data}</pre>}
</>
)
}
import { useMutation } from 'use-http'
export default function MutationComponent() {
const [todoTitle, setTodoTitle] = useState('')
// can also do it this way:
// const request = useMutation`
// or this way:
// const { data, loading, error, mutate } = useMutation`
const [data, loading, error, mutate] = useMutation`
mutation CreateTodo($todoTitle string) {
todo(title: $todoTitle) {
id
title
}
}
`
const createTodo = () => mutate({ todoTitle })
return (
<>
<input onChange={e => setTodoTitle(e.target.value)} />
<button onClick={createTodo}>Create Todo</button>
{loading ? 'Loading...' : <pre>{data}</pre>}
</>
)
}
These props are defaults used in every request inside the <Provider />
. They can be overwritten individually
import { Provider } from 'use-http'
import QueryComponent from './QueryComponent'
import MutationComponent from './MutationComponent'
function App() {
const options = {
headers: {
Authorization: 'Bearer YOUR_TOKEN_HERE'
}
}
return (
<Provider url='http://example.com' options={options}>
<QueryComponent />
<MutationComponent />
<Provider/>
)
}
This example shows how we can do authentication in the request
interceptor and how we can camelCase the results in the response
interceptor
import { Provider } from 'use-http'
import { toCamel } from 'convert-keys'
function App() {
let [token, setToken] = useLocalStorage('token')
const options = {
interceptors: {
// every time we make an http request, this will run 1st before the request is made
// url, path and route are supplied to the interceptor
// request options can be modified and must be returned
request: async (options, url, path, route) => {
if (isExpired(token)) {
token = await getNewToken()
setToken(token)
}
options.headers.Authorization = `Bearer ${token}`
return options
},
// every time we make an http request, before getting the response back, this will run
response: (response) => {
// unfortunately, because this is a JS Response object, we have to modify it directly.
// It shouldn't have any negative affect since this is getting reset on each request.
// use "eslint-disable-next-line" if you're getting linting errors.
if (response.data) response.data = toCamel(response.data)
return response
}
}
}
return (
<Provider url='http://example.com' options={options}>
<SomeComponent />
<Provider/>
)
}
This example shows how we can upload a file using useFetch
.
import useFetch from 'use-http'
const FileUploader = () => {
const [file, setFile] = useState()
const { post } = useFetch('https://example.com/upload')
const uploadFile = async () => {
const data = new FormData()
data.append('file', file)
if (file instanceof FormData) await post(data)
}
return (
<div>
{/* Drop a file onto the input below */}
<input onChange={e => setFile(e.target.files[0])} />
<button onClick={uploadFile}>Upload</button>
</div>
)
}
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.
import useFetch from 'use-http'
const App = () => {
const [name, setName] = useState('')
const { get, loading, error, response } = useFetch('http://example.com')
const handleClick = async () => {
await get('/users/1?name=true') // will return just the user's name
const text = await response.text()
setName(text)
}
return (
<>
<button onClick={handleClick}>Load Data</button>
{error && error.messge}
{loading && "Loading..."}
{name && <div>{name}</div>}
</>
)
}
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.
import useFetch from 'use-http'
const Todos = () => {
// let's say for this request, you don't want the `Accept` header at all
const { loading, error, data: todos } = useFetch(globalOptions => {
delete globalOptions.headers.Accept
return {
data: [],
...globalOptions
}
}, []) // onMount
// can also do this and overwrite the url like this
// const { loading, error, data: todos } = useFetch('https://my-new-url.com', globalOptions => {
return (
<>
{error && error.messge}
{loading && "Loading..."}
{todos && <ul>{todos.map(todo => <li key={todo.id}>{todo.title}</li>)}</ul>}
</>
)
}
const App = () => {
const options = {
headers: {
Accept: 'application/json'
}
}
return (
<Provider url='https://url.com' options={options}><Todos /></Provider>
}
Hook | Description |
---|---|
useFetch | The base hook |
useQuery | For making a GraphQL query |
useMutation | For making a GraphQL mutation |
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 |
---|---|---|
suspense | Enables Experimental React Suspense mode. example | false |
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 |
cacheLife | After a successful cache update, that cache data will become stale after this duration | 0 |
url | Allows you to set a base path so relative paths can be used for each request :) | empty string |
onNewData | Merges the current data with the incoming data. Great for pagination. | (curr, new) => new |
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 |
onAbort | Runs when the request is aborted. | empty function |
onTimeout | Called when the request times out. | empty function |
retries | When a request fails or times out, retry the request this many times. By default it will not retry. | 0 |
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 | 30000 (30 seconds) |
data | Allows you to set a default value for data | undefined |
loading | Allows you to set default value for loading | false unless the last argument of useFetch is [] |
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 |
persist | Persists data for the duration of cacheLife . If cacheLife is not set it defaults to 24h. Currently only available in Browser. | false |
const options = {
// accepts all `fetch` options such as headers, method, etc.
// enables experimental React Suspense mode
suspense: true, // defaults to `false`
// Cache responses to improve speed and reduce amount of requests
// Only one request to the same endpoint will be initiated unless cacheLife expires for 'cache-first'.
cachePolicy: 'cache-first' // 'no-cache'
// The time in milliseconds that cache data remains fresh.
cacheLife: 0,
// Allows caching to persist after page refresh. Only supported in the Browser currently.
persist: false,
// used to be `baseUrl`. You can set your URL this way instead of as the 1st argument
url: 'https://example.com',
// called when the request times out
onTimeout: () => {},
// called when aborting the request
onAbort: () => {},
// this will allow you to merge the data however you choose. Used for Pagination
onNewData: (currData, newData) => {
return [...currData, ...newData]
},
// this will tell useFetch not to run the request if the list doesn't haveMore. (pagination)
// i.e. if the last page fetched was < 15, don't run the request again
perPage: 15,
// amount of times it should retry before erroring out
retries: 3,
// amount of time before the request (or request(s) for each retry) errors out.
timeout: 10000,
// set's the default for the `data` field
data: [],
// set's the default for `loading` field
loading: false,
// typically, `interceptors` would be added as an option to the `<Provider />`
interceptors: {
request: async (options, url, path, route) => { // `async` is not required
return options // returning the `options` is important
},
response: (response) => {
return response // returning the `response` is important
}
}
}
useFetch(options)
// OR
<Provider options={options}><ResOfYourApp /></Provider>
Does your company use use-http? Consider sponsoring the project to fund new features, bug fixes, and more.
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 |
If you have feature requests, submit an issue to let us know what you would like to see!
<Suspense />
component.
.read()
to request
suspense: true
optionresponse.json()
and there is no response yetresponse.isExpired
useMutation
+ useQuery
response
see this PRresponse.formData()
and some of the other http response methods
work properlyuseSubscription
and const request = useFetch(); request.subscribe()
or something along those linesheaders
or queries
like this: const request = useFetch('https://example.com')
request
.headers({
auth: jwt // this would inline add the `auth` header
})
.query({ // might have to use .params({ }) since we're using .query() for GraphQL
no: 'way' // this would inline make the url: https://example.com?no=way
})
.get()
potential option ideas
const request = useFetch({
// Allows you to pass in your own cache to useFetch
// This is controversial though because `cache` is an option in the requestInit
// and it's value is a string. See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache
// One possible solution is to move the default `fetch`'s `cache` to `cachePolicy`.
// I don't really like this solution though.
// Another solution is to only allow the `cache` option with the `<Provider cache={new Map()} />`
cache: new Map(),
interceptors: {
// I think it's more scalable/clean to have this as an object.
// What if we only need the `route` and `options`?
request: async ({ options, url, path, route }) => {},
response: ({ response }) => {}
},
// can retry on certain http status codes
retryOn: [503],
// OR
retryOn({ attempt, error, response }) {
// retry on any network error, or 4xx or 5xx status codes
if (error !== null || response.status >= 400) {
console.log(`retrying, attempt number ${attempt + 1}`);
return true;
}
},
// This function receives a retryAttempt integer and returns the delay to apply before the next attempt in milliseconds
retryDelay({ attempt, error, response }) {
// applies exponential backoff
return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)
// applies linear backoff
return attempt * 1000
},
// these will be the exact same ones as Apollo's
cachePolicy: 'cache-and-network', 'network-only', 'cache-only', 'no-cache' // 'cache-first'
// potential idea to fetch on server instead of just having `loading` state. Not sure if this is a good idea though
onServer: true,
onSuccess: (/* idk what to put here */) => {},
onError: (error) => {},
// if you would prefer to pass the query in the config
query: `some graphql query`
// if you would prefer to pass the mutation in the config
mutation: `some graphql mutation`
retryOnError: false,
refreshWhenHidden: false,
})
resources
potential option ideas for GraphQL
const request = useQuery({ onMount: true })`your graphql query`
const request = useFetch(...)
const userID = 'some-user-uuid'
const res = await request.query({ userID })`
query Todos($userID string!) {
todos(userID: $userID) {
id
title
}
}
`
useQuery
and useMutation
đconst App = () => {
const [todoTitle, setTodoTitle] = useState('')
// if there's no <Provider /> used, useMutation works this way
const mutation = useMutation('http://example.com', `
mutation CreateTodo($todoTitle string) {
todo(title: $todoTitle) {
id
title
}
}
`)
// ideally, I think it should be mutation.write({ todoTitle }) since mutation ~= POST
const createTodo = () => mutation.read({ todoTitle })
if (!request.data) return null
return (
<>
<input onChange={e => setTodoTitle(e.target.value)} />
<button onClick={createTodo}>Create Todo</button>
<pre>{mutation.data}</pre>
</>
)
}
FAQs
- 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://w
The npm package use-http receives a total of 19,355 weekly downloads. As such, use-http popularity was classified as popular.
We found that use-http demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.