Neptunium, Promethium, Manganese

npm

Ready to take your JavaScript development to the next level? Meet npm Enterprise - the ultimate in enterprise JavaScript.Learn more »

use-http

0.1.79 • Public • Published

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

Usage

⚠️ Examples click me
Basic Usage (managed state) useFetch
import useFetch from 'use-http'
 
function Todos() {
  const [todos, setTodos] = useState([])
 
  const request = useFetch('https://example.com')
 
  // on mount, initialize the todos
  useEffect(() => {
    initializeTodos()
  }, [])
 
  async function initializeTodos() {
    const initialTodos = await request.get('/todos')
    setTodos(initialTodos)
  }
 
  async function addTodo() {
    const newTodo = await request.post('/todos', {
      title: 'no way',
    })
    setTodos(oldTodos => [...oldTodos, newTodo])
  }
 
  return (
    <>
      <button onClick={addTodo}>Add Todo</button>
      {request.error && 'Error!'}
      {request.loading && 'Loading...'}
      {todos.length > 0 && 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
  }
 
  const todos = useFetch('https://example.com/todos', options)
 
  function addTodo() {
    todos.post({
      title: 'no way',
    })
  }
 
  return (
    <>
      <button onClick={addTodo}>Add Todo</button>
      {request.error && 'Error!'}
      {request.loading && 'Loading...'}
      {(todos.data || []).length > 0 && todos.data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}
Destructured useFetch
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 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'
})
useGet, usePost, usePatch, usePut, useDelete
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 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:asdfasdfasdfasdfasdafd'
    }
  }
  
  return (
    <Provider url='http://example.com' options={options}>
      <QueryComponent />
      <MutationComponent />
    <Provider/>
  )
}
 

Overview

Hooks
Hook 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
url 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', // used to be `baseUrl`
  onMount: true
})

or

const [data, loading, error, request] = useFetch({
  // accepts all `fetch` options such as headers, method, etc.
  url: 'https://example.com', // used to be `baseUrl`
  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

Feature Requests/Ideas

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

Todos

  • 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 work with FormData
  • 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
  • Allow option to fetch on server instead of just having loading state
  • add timeout
  • add debounce
  • maybe add a retry: 3 which would specify the amount of times it should retry before erroring out
  • make GraphQL work with React Suspense
  • make GraphQL examples in codesandbox
  • Documentation:
    • show comparison with Apollo
  • maybe add syntax for inline headers like this
  const user = useFetch()
  
  user
    .headers({
      auth: jwt
    })
    .get()
 
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>
    </>
  )
}

install

npm i use-http

Downloadsweekly downloads

618

version

0.1.79

license

MIT

homepage

use-http.com

repository

Gitgithub

last publish

collaborators

  • avatar
Report a vulnerability