New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More ā†’
Socket
Sign inDemoInstall
Socket

use-http

Package Overview
Dependencies
Maintainers
1
Versions
102
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

use-http

<li>useFetch - Next.js</li> <li>useFetch - create-re

  • 0.1.89
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
21K
increased by5.02%
Maintainers
1
Weekly downloads
Ā 
Created
Source

useFetch


undefined

šŸ¶ React hook for making isomorphic http requests
Main Documentation


npm i use-http


Features

  • SSR (server side rendering) support
  • TypeScript support
  • 1 dependency (use-ssr)
  • GraphQL support (queries + mutations)
  • Provider to set default url and options
  • Request/response interceptors
  • React Native support

Usage

Examples

Basic Usage (managed state) useFetch
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>
      )}
    </>
  )
}
Basic Usage (no managed state) useFetch
import useFetch from 'use-http'

function Todos() {
  const options = { // accepts all `fetch` options
    onMount: true,  // will fire on componentDidMount (GET by default)
    data: []        // setting default for `data` as array instead of undefined
  }

  const { loading, error, data } = useFetch('https://example.com/todos', options)

  return (
    <>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}
Basic Usage with Provider
import useFetch, { Provider } from 'use-http'

function Todos() {
  const { loading, error, data } = useFetch({
    onMount: true,
    path: '/todos',
    data: []
  })

  return (
    <>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}

const App = () => (
  <Provider url='https://example.com'>
    <Todos />
  </Provider>
)
Destructured useFetch
var [request, response, loading, error] = useFetch('https://example.com')

// want to use object destructuring? You can do that too
var {
  request,
  response,
  loading,
  error,
  data,
  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')

var {
  loading,
  error,
  data,
  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

var {
  data,
  ok,
  headers,
  ...restOfHttpResponse // everything you would get in a response from an http request
} = response
Relative routes 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'
})
Abort 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>
  ))}
</>
GraphQL Query 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>}
    </>
  )
}
GraphQL Mutation 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
Query for todos
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>}
    </>
  )
}
Add a new todo
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>}
    </>
  )
}
Adding the Provider

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/>
  )
}

Request/Response Interceptors

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 camelCase from 'camelcase-keys-recursive'

function App() {
  let [token] = useLocalStorage('token')
  
  const options = {
    interceptors: {
      // every time we make an http request, this will run 1st before the request is made
      request: async (options) => {
        if (isExpired(token)) token = await getNewToken()
        options.headers.Authorization = `Bearer ${token}`
        return options
      },
      // every time we make an http request, before getting the response back, this will run
      response: (response) => camelCase(response)
    }
  }
  
  return (
    <Provider url='http://example.com' options={options}>
      <SomeComponent />
    <Provider/>
  )
}

File Uploads (FormData)

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>
  )
}

Overview

Hooks

HookDescription
useFetchThe base hook
useQueryFor making a GraphQL query
useMutationFor making a GraphQL mutation

Options

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 />

OptionDescriptionDefault
urlAllows you to set a base path so relative paths can be used for each request :)empty string
onMountOnce the component mounts, the http request will run immediatelyfalse
onUpdateThis is essentially the same as the dependency array for useEffect. Whenever one of the variables in this array is updated, the http request will re-run.[]
retriesWhen a request fails or times out, retry the request this many times. By default it will not retry.0
timeoutThe request will be aborted/cancelled after this amount of time. This is also the interval at which retries will be made at. in milliseconds30000
(30 seconds)
dataAllows you to set a default value for dataundefined
loadingAllows you to set default value for loadingfalse unless onMount === true
interceptors.requestAllows you to do something before an http request is sent out. Useful for authentication if you need to refresh tokens a lot.undefined
interceptors.responseAllows you to do something after an http response is recieved. Useful for something like camelCasing the keys of the response.undefined
useFetch({
  // accepts all `fetch` options such as headers, method, etc.
  url: 'https://example.com',     // used to be `baseUrl`
  onMount: true,
  onUpdate: []                    // everytime a variable in this array is updated, it will re-run the request (GET by default)
  retries: 3,                     // amount of times it should retry before erroring out
  timeout: 10000,                 // amount of time before the request (or request(s) for retries) errors out.
  data: [],                       // default for `data` field
  loading: false,                 // default for `loading` field
  interceptors: {                 // typically, `interceptors` would be added as an option to the `<Provider />`
    request: async (options) => { // `async` is not required
      return options              // returning the `options` is important
    },
    response: (response) => {
      return response             // returning the `response` is important
    }
  }
})

Feature Requests/Ideas

If you have feature requests, let's talk about them in this issue!

Todos

  • add browser support to docs (currently does not support ie 11)
  • tests
    • tests for SSR
    • tests for FormData (can also do it for react-native at same time. see here)
    • tests for GraphQL hooks useMutation + useQuery
  • make this a github package
  • Make work with React Suspense current example WIP
  • get it all working on a SSR codesandbox, this way we can have api to call locally
  • make GraphQL work with React Suspense
  • make GraphQL examples in codesandbox
  • Documentation:
    • show comparison with Apollo
  • Dedupe requests done to the same endpoint. Only one request to the same endpoint will be initiated. ref
  • Cache responses to improve speed and reduce amount of requests
  • maybe add syntax for middle helpers for inline headers or queries like this:
  const request = useFetch('https://example.com')
  
  request
    .headers({
      auth: jwt      // this would inline add the `auth` header
    })
    .query({
      no: 'way'      // this would inline make the url: https://example.com?no=way
    })
    .get()
  • maybe add snake_case -> camelCase option to <Provider />. This would convert all the keys in the response to camelCase. Not exactly sure how this syntax should look because what if you want to have this only go 1 layer deep into the response object. Or if this is just out of scope for this library.
<Provider responseKeys={{ case: 'camel' }}><App /></Provider>
  • potential option ideas (for retryOn)
const request = useFetch({
  retryOn: [503],          // can retry on certain http status codes
  // 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;
    }
  },
  onTimeout: () => {},     // called when the last `retry` is made and times out
  onAbort: () => {},       // called when aborting the request
  onServer: true,          // potential idea to fetch on server instead of just having `loading` state. Not sure if this is a good idea though
  query: `some graphql query`       // if you would prefer to pass the query in the config
  mutation: `some graphql mutation` // if you would prefer to pass the mutation in the config
})
  • potential option ideas for GraphQL
const request = useQuery({ onMount: true })`your graphql query`
  • add callback to completely overwrite options. 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
const request = useFetch('https://url.com', globalOptions => {
  delete globalOptions.headers.Authorization
  return globalOptions
})
  • make code editor plugin/package/extension that adds GraphQL syntax highlighting for useQuery and useMutation šŸ˜Š
The Goal With Suspense (not implemented yet)
import React, { Suspense, unstable_ConcurrentMode as ConcurrentMode, useEffect } from 'react'

function WithSuspense() {
  const suspense = useFetch('https://example.com')

  useEffect(() => {
    suspense.read()
  }, [])

  if (!suspense.data) return null

  return <pre>{suspense.data}</pre>
}

function App() (
  <ConcurrentMode>
    <Suspense fallback="Loading...">
      <WithSuspense />
    </Suspense>
  </ConcurrentMode>
)
GraphQL with Suspense (not implemented yet)
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>
    </>
  )
}

Keywords

FAQs

Package last updated on 07 Oct 2019

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with āš”ļø by Socket Inc