use-invariant
TypeScript icon, indicating that this package has built-in type declarations

0.1.70 • Public • Published

useFetch

🐶 React hook for making isomorphic http requests

undefined undefined Known Vulnerabilities Known Vulnerabilities undefined undefined

Need to fetch some data? Try this one out. It's an isomorphic fetch hook. That means it works with SSR (server side rendering).


A note on the documentation below. Many of these examples could have performance improvements using useMemo and useCallback, but for the sake of the beginner/ease of reading, they are left out.

Features

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

Examples

Installation

yarn add use-http    or    npm i -S use-http

Usage

Basic Usage

import useFetch from 'use-http'
 
function Todos() {
  const options = { // accepts all `fetch` options
    onMount: true // will fire on componentDidMount
  }
 
  const todos = useFetch('https://example.com/todos', options)
 
  function addTodo() {
    todos.post({
      title: 'no way',
    })
  }
 
  if (todos.error) return 'Error!'
  if (todos.loading) return 'Loading...'
 
  return (
    <>
      <button onClick={addTodo}>Add Todo</button>
      {todos.data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}

Destructured

var [data, loading, error, request] = useFetch('https://example.com')
 
// want to use object destructuring? You can do that too
var { data, loading, error, request } = useFetch('https://example.com')

Relative routes

const request = useFetch({
  baseUrl: 'https://example.com'
})
 
request.post('/todos', {
  no: 'way'
})

Helper hooks

import { useGet, usePost, usePatch, usePut, useDelete } from 'use-http'
 
const [data, loading, error, patch] = usePatch({
  url: 'https://example.com',
  headers: {
    'Accept': 'application/json; charset=UTF-8'
  }
})
 
patch({
  yes: 'way',
})

Abort

const githubRepos = useFetch({
  baseUrl: `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

 
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

 
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

The Provider allows us to set a default url, options (such as headers) and so on.

Query for todos
import { Provider, useQuery, useMutation } from 'use-http'
 
function QueryComponent() {
  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
function MutationComponent() {
  const [todoTitle, setTodoTitle] = useState('')
  
  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

function App() {
 
  const options = {
    headers: {
      Authorization: 'Bearer:asdfasdfasdfasdfasdafd'
    }
  }
  
  return (
    <Provider url='http://example.com' options={options}>
      <QueryComponent />
      <MutationComponent />
    <Provider/>
  )
}
 

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

Hooks

Option Description
useFetch The base hook
useGet Defaults to a GET request
usePost Defaults to a POST request
usePut Defaults to a PUT request
usePatch Defaults to a PATCH request
useDelete Defaults to a DELETE request
useQuery For making a GraphQL query
useMutation For making a GraphQL mutation

Options

This is exactly what you would pass to the normal js fetch, with a little extra.

Option Description Default
onMount Once the component mounts, the http request will run immediately false
baseUrl Allows you to set a base path so relative paths can be used for each request :) empty string
const {
  data,
  loading,
  error,
  request,
  get,
  post,
  patch,
  put,
  delete  // don't destructure `delete` though, it's a keyword 
  del,    // <- that's why we have this (del). or use `request.delete`
  abort,
  query,  // GraphQL
  mutate, // GraphQL
= useFetch({
  // accepts all `fetch` options such as headers, method, etc.
  url: 'https://example.com',
  baseUrl: 'https://example.com',
  onMount: true
})

or

const [data, loading, error, request] = useFetch({
  // accepts all `fetch` options such as headers, method, etc.
  url: 'https://example.com',
  baseUrl: 'https://example.com',
  onMount: true
})
 
const {
  get,
  post,
  patch,
  put,
  delete  // don't destructure `delete` though, it's a keyword 
  del,    // <- that's why we have this (del). or use `request.delete`
  abort,
  query,  // GraphQL
  mutate, // GraphQL
= request

Credits

use-http is heavily inspired by the popular http client axios

Feature Requests/Ideas

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

Todos

  • port to typescript
  • badges
  • if no url is specified, and we're in the browser, use window.location.origin
  • support for a global context config where you can set base url's (like Apollo's client) but better 😉
  • add GraphQL useQuery, useMutation
  • Make work with React Suspense current example WIP
  • make work with FormData
  • get it all working on a SSR codesandbox, this way we can have api to call locally
  • Allow option to fetch on server instead of just having loading state
  • add timeout
  • add debounce
  • maybe add a retries: 3 which would specify the amount of times it should retry before erroring out
  • tests
  • ERROR handling:
    • if doing useQuery('my query') without specifiying a URL in the Provider, throw error
    • make sure options (as 2nd param) to all hooks is an object, if not invariant/throw error
  • add array destructuring return types
  • github page/website for docs + show comparison with Apollo
  • fix code so Maintainability is A
  • optimize badges see awesome badge list
  • make GraphQL work with React Suspense
  • make GraphQL examples in codesandbox
  • make cool logo 😜 I kinda want it to move like this one
  • maybe add syntax for custom headers like this
  const user = useFetch()
  
  user
    .headers({
      auth: jwt
    })
    .get()
 

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

Package Sidebar

Install

npm i use-invariant

Weekly Downloads

1

Version

0.1.70

License

MIT

Unpacked Size

61.6 kB

Total Files

44

Last publish

Collaborators

  • alex-cory