use-invariant
v0.1.70
Published
- <a target="_blank" rel="noopener noreferrer" href='https://codesandbox.io/s/usefetch-in-nextjs-nn9fm'>Example - Next.js</a> - <a target="_blank" rel="noopener noreferrer" href='https://codesandbox.io/embed/km04k9k9x5'>Examples - create-react-app</a>
Downloads
15
Maintainers
Keywords
Readme
Features
- SSR (server side rendering) support
- TypeScript support
- 1 dependency (use-ssr)
- GraphQL support (queries + mutations)
- Provider to set default
url
andoptions
Examples
- Example - Next.js
- Examples - create-react-app
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
- [x] port to typescript
- [x] badges
- [X] if no url is specified, and we're in the browser, use
window.location.origin
- [X] support for a global context config where you can set base url's (like Apollo's
client
) but better 😉 - [X] 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 notinvariant
/throw error
- [ ] if doing
- [ ] 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>
</>
)
}