Coder Social home page Coder Social logo

ava / use-http Goto Github PK

View Code? Open in Web Editor NEW
2.3K 18.0 113.0 7.83 MB

🐶 React hook for making isomorphic http requests

Home Page: https://use-http.com

License: MIT License

TypeScript 99.13% JavaScript 0.87%
react fetch suspense graphql ssr react-hooks react-hook reacthook react-suspense rest-client

use-http's Introduction

use-http logo


useFetch


undefined

🐶 React hook for making isomorphic http requests
Main Documentation


npm i use-http


Features

  • SSR (server side rendering) support
  • TypeScript support
  • 2 dependencies (use-ssr, urs)
  • GraphQL support (queries + mutations)
  • Provider to set default url and options
  • Request/response interceptors
  • React Native support
  • Aborts/Cancels pending http requests when a component unmounts
  • Built in caching
  • Persistent caching support
  • Suspense(experimental) support
  • Retry functionality

Usage

Examples + Videos

  • useFetch - managed state, request, response, etc.
  • useFetch - request/response interceptors
  • useFetch - retries, retryOn, retryDelay
  • useFetch - abort, timeout, onAbort, onTimeout
  • useFetch - persist, cache
  • useFetch - cacheLife, cachePolicy
  • useFetch - suspense (experimental)
  • useFetch - pagination
  • useQuery - GraphQL
  • useFetch - Next.js
  • useFetch - create-react-app
Basic Usage Managed State 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 { get, post, response, loading, error } = useFetch('https://example.com')

  useEffect(() => { initializeTodos() }, []) // componentDidMount
  
  async function initializeTodos() {
    const initialTodos = await get('/todos')
    if (response.ok) setTodos(initialTodos)
  }

  async function addTodo() {
    const newTodo = await post('/todos', { title: 'my new todo' })
    if (response.ok) setTodos([...todos, newTodo])
  }

  return (
    <>
      <button onClick={addTodo}>Add Todo</button>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {todos.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      ))}
    </>
  )
}

Basic Usage Auto-Managed State 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() {
  const options = {} // these options accept all native `fetch` options
  // the last argument below [] means it will fire onMount (GET by default)
  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>
      ))}
    </>
  )
}
Suspense Mode(experimental) Auto-Managed State

Can put suspense in 2 places. Either useFetch (A) or Provider (B).

import useFetch, { Provider } from 'use-http'

function Todos() {
  const { data: todos = [] } = useFetch('/todos', {
    suspense: true // A. can put `suspense: true` here
  }, []) // onMount

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

Suspense Mode(experimental) Managed State

Can put suspense in 2 places. Either useFetch (A) or Provider (B). Suspense mode via managed state is very experimental.

import useFetch, { Provider } from 'use-http'

function Todos() {
  const [todos, setTodos] = useState([])
  // A. can put `suspense: true` here
  const { get, response } = useFetch({ suspense: true })

  const loadInitialTodos = async () => {
    const todos = await get('/todos')
    if (response.ok) setTodos(todos)
  }

  // componentDidMount
  useEffect(() => {
    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
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





Pagination + 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(`/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
  }, [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>
)

Destructured useFetch

⚠️ Do not destructure the response object! Details in this video. 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`
  head,
  options,
  connect,
  trace,
  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

Relative routes useFetch
var request = useFetch('https://example.com')

request.post('/todos', {
  no: 'way'
})

Abort useFetch
const { get, abort, loading, data: repos } = useFetch('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 => get(encodeURI(e.target.value))

<>
  <input onChange={searchGithubRepos} />
  <button onClick={abort}>Abort</button>
  {loading ? 'Loading...' : repos.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 { 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: async ({ response, request }) => {
        // 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.
        const res = response
        if (res.data) res.data = toCamel(res.data)
        return res
      }
    }
  }
  
  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>
  )
}
Handling Different Response Types

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

Overwrite/Remove Options/Headers Set in Provider

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('/todos', globalOptions => {
    delete globalOptions.headers.Accept
    return globalOptions
  }, []) // onMount
  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>
}
Retries retryOn & retryDelay

In this example you can see how retryOn will retry on a status code of 305, or if we choose the retryOn() function, it returns a boolean to decide if we will retry. With retryDelay we can either have a fixed delay, or a dynamic one by using retryDelay(). Make sure retries is set to at minimum 1 otherwise it won't retry the request. If retries > 0 without retryOn then by default we always retry if there's an error or if !response.ok. If retryOn: [400] and retries > 0 then we only retry on a response status of 400.

import useFetch from 'use-http'

const TestRetry = () => {
  const { response, get } = useFetch('https://httpbin.org/status/305', {
    // make sure `retries` is set otherwise it won't retry
    retries: 1,
    retryOn: [305],
    // OR
    retryOn: async ({ attempt, error, response }) => {
      // returns true or false to determine whether to retry
      return error || response && response.status >= 300
    },

    retryDelay: 3000,
    // OR
    retryDelay: ({ attempt, error, response }) => {
      // exponential backoff
      return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)
      // linear backoff
      return attempt * 1000
    }
  })

  return (
    <>
      <button onClick={() => get()}>CLICK</button>
      <pre>{JSON.stringify(response, null, 2)}</pre>
    </>
  )
}

Overview

Hooks

Hook Description
useFetch The base hook
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. 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
cacheLife After a successful cache update, that cache data will become stale after this duration 0
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
data Allows you to set a default value for data undefined
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
loading Allows you to set default value for loading false unless the last argument of useFetch is []
onAbort Runs when the request is aborted. empty function
onError Runs when the request get's an error. If retrying, it is only called on the last retry attempt. empty function
onNewData Merges the current data with the incoming data. Great for pagination. (curr, new) => new
onTimeout Called when the request times out. empty function
persist Persists data for the duration of cacheLife. If cacheLife is not set it defaults to 24h. Currently only available in Browser. false
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
responseType This will determine how the data field is set. If you put json then it will try to parse it as JSON. If you set it as an array, it will attempt to parse the response in the order of the types you put in the array. Read about why we don't put formData in the defaults in the yellow Note part here. ['json', 'text', 'blob', 'readableStream']
retries When a request fails or times out, retry the request this many times. By default it will not retry. 0
retryDelay You can retry with certain intervals i.e. 30 seconds 30000 or with custom logic (i.e. to increase retry intervals). 1000
retryOn You can retry on certain http status codes or have custom logic to decide whether to retry or not via a function. Make sure retries > 0 otherwise it won't retry. []
suspense Enables Experimental React Suspense mode. example false
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. If set to 0, it will not timeout except for browser defaults. 0
const options = {
  // accepts all `fetch` options such as headers, method, etc.

  // The time in milliseconds that cache data remains fresh.
  cacheLife: 0,

  // 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'
  
  // set's the default for the `data` field
  data: [],

  // 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: async ({ response, request }) => {
      // notes:
      // - `response.data` is equivalent to `await response.json()`
      // - `request` is an object matching the standard fetch's options
      return response // returning the `response` is important
    }
  },

  // set's the default for `loading` field
  loading: false,
  
  // called when aborting the request
  onAbort: () => {},
  
  // runs when an error happens.
  onError: ({ error }) => {},

  // this will allow you to merge the `data` for pagination.
  onNewData: (currData, newData) => {
    return [...currData, ...newData] 
  },
  
  // called when the request times out
  onTimeout: () => {},
  
  // 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,

  // Allows caching to persist after page refresh. Only supported in the Browser currently.
  persist: false,

  // this would basically call `await response.json()`
  // and set the `data` and `response.data` field to the output
  responseType: 'json',
  // OR can be an array. It's an array by default.
  // We will try to get the `data` by attempting to extract
  // it via these body interface methods, one by one in
  // this order. We skip `formData` because it's mostly used
  // for service workers.
  responseType: ['json', 'text', 'blob', 'arrayBuffer'],

  // amount of times it should retry before erroring out
  retries: 3,

  // The time between retries
  retryDelay: 10000,
  // OR
  // Can be a function which is used if we want change the time in between each retry
  retryDelay({ attempt, error, response }) {
    // exponential backoff
    return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)
    // linear backoff
    return attempt * 1000
  },

  // make sure `retries` is set otherwise it won't retry
  // can retry on certain http status codes
  retryOn: [503],
  // OR
  async 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;
    }
  },

  // enables experimental React Suspense mode
  suspense: true, // defaults to `false`
  
  // amount of time before the request get's canceled/aborted
  timeout: 10000,
}

useFetch(options)
// OR
<Provider options={options}><ResOfYourApp /></Provider>

Who's using use-http?

Does your company use use-http? Consider sponsoring the project to fund new features, bug fixes, and more.

Browser Support

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!

IE / Edge
Edge
Firefox
Firefox
Chrome
Chrome
Safari
Safari
Opera
Opera
12+ last 2 versions last 2 versions last 2 versions last 2 versions

Feature Requests/Ideas

If you have feature requests, submit an issue to let us know what you would like to see!

Todos

  • OSS analytics

  • contributors

  • prefetching

  • global cache state management

  • optimistic updates

  • persist support for React Native

  • better loading state management. When using only 1 useFetch in a component and we use Promise.all([get('/todos/1'), get('/todos/2')]) then don't have a loading true, loading false on each request. Just have loading true on 1st request, and loading false on last request.

  • is making a gitpod useful here? 🤔

  • suspense

    • triggering it from outside the <Suspense /> component.
      • add .read() to request
      • or make it work with just the suspense: true option
      • both of these options need to be thought out a lot more^
    • tests for this^ (triggering outside)
    • cleanup tests in general. Snapshot tests are unpredictably not working for some reason.
  • maybe add translations like this one

  • maybe add contributors all-contributors

  • add sponsors similar to this

  • Error handling

    • if calling response.json() and there is no response yet
  • tests

    • tests for SSR
    • tests for react native see here
    • tests for GraphQL hooks useMutation + useQuery
    • tests for stale response see this PR
    • tests to make sure response.formData() and some of the other http response methods work properly
    • the onMount works properly with all variants of passing useEffect(fn, [request.get]) and not causing an infinite loop
    • async tests for interceptors.response
    • aborts fetch on unmount
    • does not abort fetch on every rerender
    • retryDelay and timeout are both set. It works, but is annoying to deal with timers in tests. resource
    • timeout with retries > 0. (also do retires > 1) Need to figure out how to advance timers properly to write this and the test above
  • take a look at how react-apollo-hooks work. Maybe ad useSubscription and const request = useFetch(); request.subscribe() or something along those lines

  • make this a github package

  • Documentation:

    • show comparison with Apollo
    • figure out a good way to show side-by-side comparisons
    • show comparison with Axios
  • potential option ideas

    const request = useFetch({
      graphql: {
        // all options can also be put in here
        // to overwrite those of `useFetch` for
        // `useMutation` and `useQuery`
      },
      // by default this is true, but if set to false
      // then we default to the responseType array of trying 'json' first, then 'text', etc.
      // hopefully I get some answers on here: https://bit.ly/3afPlJS
      responseTypeGuessing: true,
    
      // 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(),
      // 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 */) => {},
      // 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`
      refreshWhenHidden: false,
    })
    
    
    // potential for causing a rerender after clearing cache if needed
    request.cache.clear(true)
  • 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
        }
      }
    `
  • make code editor plugin/package/extension that adds GraphQL syntax highlighting for useQuery and useMutation 😊

  • add React Native test suite

use-http's People

Contributors

alex-cory avatar alexburner avatar ashubham avatar cktang88 avatar dagda1 avatar dependabot[bot] avatar eagleera avatar g-plane avatar gerhut avatar goodrone avatar grafikart avatar greenkeeper[bot] avatar ignusg avatar jasonman34 avatar krishnasaga avatar marcod1419 avatar maxquinn avatar mcclayton avatar mogelbrod avatar mongrain avatar nanyang24 avatar patrickdelancy avatar romiem avatar rrostt avatar sbichenko avatar sgtwilko avatar theduc avatar toburger avatar vitaliyandrushko avatar yassienw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

use-http's Issues

Weird types

Describe the bug

export interface ReqBase<TData> {
    data: TData | undefined;
    loading: boolean;
    error: Error;
}

That should be replaced with

type ReqBase<TData> = 
  | {
    data: undefined.
    error: undefined,
    loading: false,
  }
  | {
    data: TData,
    error: undefined,
    loading: true,
  }
  | {
    data: undefined,
    error: Error,
    loading: true,
  };

Additional Response Data

Scenario

I'm creating a simple CRUD app with authentication. My API uses HTTP status codes and response headers to indicate certain information. Some status code examples:

  • Check if user is logged in when they aren't: GET /auth => 403
  • Login with invalid credentials: POST /auth => 403

Problem

Using use-http to make HTTP requests, I'm unable to determine the HTTP status or response headers of my requests, forcing me to modify my API to include these values in the response body (not so easy when consuming a third party API).

Proposal

Within the useFetch output, include some of the values from fetch's Response.
Some ideas:

  • Just pass back fetch's response object directly: const [myData, isLoading, error, request, response] = useFetch<IMyDataType>('/api/path')
  • Extract values from the response and pass them back individually: const { responseStatusText, responseHeaders, responseOk } = useFetch<IMyDataType>('/api/path')
  • Extract values from the response and pass them back in a custom response object: const { useHttpResponse } = useFetch<IMyDataType>('/api/path')

Is there a solution I'm missing? Or does anyone have any thoughts?

React Native Support

It is really helpful if it can support react native as well.

But as of now it will not get any response, without throwing any errors or going to finally {} logic:

// useFetch.js (after transpile, in debugger)
// line 76
// logic ends here and no further
const response = yield fetch(url + query, Object.assign({ method }, options, { headers: Object.assign({ 'Content-Type': 'application/json' }, options.headers) }));

My component code is here and it works well in web.
This seems like a fetch issue for React Native, any ideas?

Provider interceptor uses cached values?

I have created a custom Provider with (global) options to send certain headers on all requests via an interceptor. This interceptor should get a value from a context and send it with the headers, but it doesn't seem to update to the new value.

Created a CodeSandbox here

When you run it and open inspector to check the network calls, you can see that the x-token header remains null on each call, even though the 'token' value is updated in the context as showed in the console and on the component render.

The funny thing is, if I place the exact same interceptor code in the useFetch hook itself, it does send the correct header value.
Like so:

	const [request, response] = useFetch({
		interceptors: {
			request: options => {
				console.log("Interceptor called with token", state.token);
				options.headers["x-token"] = state.token;
				return options;
			}
		}
	});

Error should be reseted in every request.

Hi!

useHttp is really easy and cool to use. Congrats!
I would like to expose an issue I'm experimenting. I have something like this:

function SomeContainer() {
  const { error, request } = useFetch('https://...');
  const [data, setData] = useState(null);
  const [query, setQuery] = useState({ page: 1 });

  useEffect(() => {
    // ... A wrapper to allow using async/await
    const serializedQuery = queryString.stringify(query);
    const data = await request.get(`/some/endpoint/${serializedQuery}`);
    setData(data);
  }, [request, query]);

  const onQueryChange = nextQuery => {
    setQuery(nextQuery);
  };

  if (error) {
    return 'Something went wrong';
  }
  if (loading) {
    return 'Loading ...';
  }
  return <SomeDataComponent data={data}  onQueryChange={onQueryChange} />
}

Suppose that:

  1. The user has internet connection. Everything works fine. The result is that 'Loading ...' text appears first and then SomeDataComponent is rendered.
  2. The user lose connection.
  3. The user modifies the query and onQueryChange is called causing a re-render. That re-render executes the useEffect that will make a new fetch. The result of that fetch will be an error and we will see the text 'Something went wrong' in the screen.
  4. The user is online again.
  5. The user modify the query again so a new re-fetch is performed. In this scenario, in which the internet is reachable, we won't see SomeDataComponent rendered again. Instead we will stuck on 'Something went wrong'.

So to sum up: the error state is not reseted across requests calls.

Unexpected error

Hello I have an issue when using the useFetch hook to fire an http call.
After calling the hook on mount, the data prop is empty and I get the following in the error prop: TypeError: "NetworkError when attempting to fetch resource.

In the browser console the network call succeeded with code 200 and returns a valid JSON response.

Am I missing something the error doesn't tell me ? Or could it be a bug ?

Avoid sending ContentType with GET requests

Describe the bug
useFetch sends a content type header (application/json by default) with GET requests. This should not happend and is making express fail by default

To Reproduce
Make a GET request without any options, use-http sends a content type header of "application/json"

Expected behavior
ContentType header should not be present when making get requests

Retry request in interceptors response

Usually servers handles token expiration and then with 401 response tells the client to refresh token.
In the examples this is handled in request interceptor, but is there a way to handle it response and retry all failed requests?

SSR - Weird issue with 'loading'

Describe the bug
Server side, I'm getting loading === true even if I'm not firing any request.
I tried putting { onMount: false, loading: false }.

Expected behavior
Loading should be false if no request is made. Moreover, it seems the lib does not do anything server side, so loading should be ever set.

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.8.21 to 16.8.22.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of react is breaking the build 🚨

There have been updates to the react monorepo:

    • The devDependency react was updated from 16.8.6 to 16.9.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

This monorepo update includes releases of one or more dependencies which all belong to the react group definition.

react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

Release Notes for 16.9.0 (August 8, 2019)

React

  • Add <React.Profiler> API for gathering performance measurements programmatically. (@bvaughn in #15172)
  • Remove unstable_ConcurrentMode in favor of unstable_createRoot. (@acdlite in #15532)

React DOM

React DOM Server

  • Fix incorrect output for camelCase custom CSS property names. (@bedakb in #16167)

React Test Utilities and Test Renderer

Artifacts

• react: https://unpkg.com/[email protected]/umd/
• react-art: https://unpkg.com/[email protected]/umd/
• react-dom: https://unpkg.com/[email protected]/umd/
• react-is: https://unpkg.com/[email protected]/umd/
• react-test-renderer: https://unpkg.com/[email protected]/umd/
• scheduler: https://unpkg.com/[email protected]/umd/

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

onUpdate doesn't work with managed state

I wanna make new request. It works with onUpdate for unmanaged state.

const [isUpdate, updateState] = useState(false)
const {loading, error, data} = useFetch(
    'url',
    {
      timeout: 15000,
      onMount: true,
      onUpdate: [isUpdate],
      data: [],
    },
  )

But for managed state, it doesn't:

const [isUpdate, updateState] = useState(false)
  const [req, res] = useFetch('domain', {
      timeout: 15000,
      onUpdate: [isUpdate],
    },)
  async function fetch() {
    const data = await req.get(path)
    ....
  }

...
<TouchableOpacity
        onPress={() => {
          updateState(!isUpdate)
          fetch(true)
        }}>
<Text>reset</Text>
</TouchableOpacity>

After changing isUpdate and make another fetch() request. The result is still the same.

An in-range update of @typescript-eslint/parser is breaking the build 🚨

The devDependency @typescript-eslint/parser was updated from 2.19.0 to 2.19.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@typescript-eslint/parser is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

Release Notes for v2.19.1

2.19.1 (2020-02-10)

Bug Fixes

  • eslint-plugin: [unbound-method] blacklist a few unbound natives (#1562) (4670aab)
  • typescript-estree: ts returning wrong file with project references (#1575) (4c12dac)
Commits

The new version differs by 5 commits.

  • 1c8f0df chore: publish v2.19.1
  • 4c12dac fix(typescript-estree): ts returning wrong file with project references (#1575)
  • e9cf734 docs(eslint-plugin): fix typo in readme
  • 10d86b1 docs(eslint-plugin): [no-dupe-class-members] fix typo (#1566)
  • 4670aab fix(eslint-plugin): [unbound-method] blacklist a few unbound natives (#1562)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Access the `.fetch` response directly to check for `response.ok` ?

Hi there!

Great work on the library- It looks to have almost all of what we need, but I've been playing with it and I've found an edge-case that I'd love some help with.

In our current implementations of our own hand-rolled useFetch, we have something that checks for any response.status that is not in the 200-299 range, ie: !response.ok:

const isOK = response => {
  if (!response.ok) {
    throw response
  }

  return response
}

const toJSON = response => response.json()

return fetch(`${config.BASE_API}/${endpoint}`, finalHeaders)
         .then(isOK)
         .then(toJSON)

This is so that we can basically call anything that's not a "200" an "error", as far as our front-end is concerned. We've got cases in our backend where we'd be receiving 422's, or 401's as responses, and since fetch doesn't error on those by default, we check for response.ok

Unfortunately due to the fact that use-http does all the response parsing and checking internally, by the time I get to the response data in the interceptors, I've already lost the response.ok & response.status objects etc.

Is there a way around this with the current implementation? It'd be nice if there was a way to specify interceptions even earlier, for this kind of situation, so that our 422's return to the error object rather than the data one.

Alternatively, happy to discuss other ways to solve this issue, even if I'm going about this kind of wrong.

Thanks!

Async response

https://codesandbox.io/embed/usefetch-provider-requestresponse-interceptors-tg56u

@alex-cory I'm quite sure this is not necessarily a bug but rather conceptual issue I've run into:
Weirdly, the first time an async request fires my console.log(response) (L16) returns an empty object {}. Only if I run the request again I'll get the actual Response. Could you lead me in the right direction on how to retrieve the response once it's here and do something with it?

What I'm trying to achieve:
Our API provides an endpoint for submitting form data.
Now if server-side validation fails, the response will have the exact same keys as my request payload had, except there will be error messages as values. If there are errors, the response status code will be 400. Now what I'm trying to do is to somehow find out if !response.ok or response.status === 400 (from the response), so I can map potential errors to actual error components in my UI.

I've seen similar issues (accessing response from fetch) but I'm not sure why I would need an interceptor in this case.

Assign value to error on interceptors

Hi guys,

I would like to know if makes sense this possibility of assigning a value to the error on interceptors, see an example;

I need to apply a normalize function called callable depending on the type that is in types

This function would be applied before because of the data from useFetch destructuring should already have this value normalized.

The useFetch already has a possibility to set errors like the setError example below?

const { get, error, loading, data, abort } = useFetch(types[type].url,  {
    headers: getHeaders(types[type].id),
    loading: true,
    interceptors: {
      response: data => {
        try {
          types[type].callable(data)
        } catch(error) {
          setError(error)
        }
      }
    }
  }
)

onUpdate doesn't work

Hi,
I try the pagination example in my App but the fetch is done one time only on onMount. When I update page state nothing is done. Checking the Network tab in Google Chrome DevTools any other request is done.
This is my code

export default function Reservation() {
  const [page, setPage] = useState(1);

  const { loading, error, data: reservations } = useFetch(
    {
      path: `${endpointShuttles}?page=${page}`,
      onNewData: (currReservations, newReservations) => [...currReservations, ...newReservations.data],
      data: [],
    },
    [page],
  ); // onMount AND onUpdate whenever `page` changes

  // Used to test if useEffect is fired
  useEffect(() => console.log('useEffect page', page), [page]);

  return (
    <>
      <h1>Pagination</h1>
      {error && 'Error!'}

      <ul>{reservations && reservations.map((user, i) => <li key={i}>{user.shuttleCode}</li>)}</ul>
      <button type="submit" onClick={() => setPage(page + 1)}>
        {loading ? 'Loading...' : 'Load Data'}
      </button>
    </>
  );
}

The server return 200 with the correct data.
The useEffect added to test if page change is fired and console.log woks fine.
In upper component I use Provider with some options and custom interceptors but in the request interceptor the console.log is fired only at onMount.

It seems that the change of page state is not triggered.

Someone have any idea?

Thanks in advance,
Samuele

useGet not working from 0.1.66 version

Last working version is 0.1.65. Situation looks like url has not been passed to request function, because request goes to current location(localhost).

Example of use:
const [users, usersLoading] = useGet('https://api.example.com/v2/users', { onMount: true })

Next JS Support ?

Hello there... Does it support next.js? If so, then will you please provide an example?

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.8.18 to 16.8.19.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of @types/react is breaking the build 🚨

The devDependency @types/react was updated from 16.8.23 to 16.8.24.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

How to trigger a refresh?

How would I trigger an update or requery of data?

I have a data table which I load with initial data I get using useFetch. Then in that table I have a modal to create a new item. I'm trying to re-load my dataTable when the new item is created.

I tried passing get as a prop to my form so on submit I'd call get() but get returns undefined.

This is what I have so far

  const { request, response, loading, error, data, get } = useFetch(
    `${apiPath}/mydata`,
    {
      // accepts all `fetch` options
      data: [] // default for `data` will be an array instead of undefined
    },
    []
  );

  const tableData = useMemo(
    () => (data || []).map(r => ({ ...r, questions: r.questions.length })),
    [data]
  );

  // Handle different api states
  // if (networkStatus === 4) return 'Refetching!';
  if (loading) return <p>Loading...</p>;
  if (error) throw Error(error);

  // React table requires memoized data
  // Return table with data
  return (
    <Box padding={1}>
      <ReactTable
        columns={columns}
        data={tableData}
        component={
          <FullScreenModal
            title="Create New ScoreCard"
            content={<ScoreCardForm toggleScorecardForm={toggleModal} />}
            open={isShowing}
            toggle={toggleModal}
            handleRefresh={get}
          />
        }
      />
    </Box>

Feature request: consider allowing url to be undefined at init

Hello! ✋

The use case for the subkject is that right now I cannot do this:

let [request, response] = useFetch(); // useFetch init

const f = async () => await request.get('/unknown-endpoint-url-at-usefetch-init');
useEffect(() => f());

Instead I have to init with the bare minimum:

let [request, response] = useFetch('/'); // useFetch init

const f = async () => await request.get('unknown-endpoint-url-at-usefetch-init');
useEffect(() => f());

but that might be inconsistent with the rest of the request.get() calls across the app.

An empty string, such as useFetch('') would also be awesome to have.

I'm aware that most of the time init is done by passing something like /api, or the entire hostname for production, but for local development it's more ergonomice to not to have to separate the URL only to conform to useFetch's non-empty string contract.

An in-range update of @types/jest is breaking the build 🚨

The devDependency @types/jest was updated from 24.0.16 to 24.0.17.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

@types/jest is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • now: Deployment has failed (error: #b92DczE) (Details).
  • ci/circleci: test: Your tests passed on CircleCI! (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

New requests not fired when dependency array changes

Hi,

Thanks for the excellent work on this library!

I ran into some issues today, and I'm not sure that this is because of some bug, or simply because I have misunderstood how the API is supposed to work. The case has to do with firing new requests as entries in the dependency array changes.

For instance, I was expecting the following to fire a new request whenever id changes:

const stuff = useFetch(`/user${id}.json`, [id]);

What happens is that the request is initially fired on mount, but as id changes, nothing happens.

I've probably misunderstood how this is supposed to work, but if not then this might be some bug that I'm hitting. I have a complete example reproducing it available here: https://codesandbox.io/s/usefetch-dependency-array-slsb9?fontsize=14&hidenavigation=1&theme=dark

Thanks,
Jesper

error should be set when server response with an error

Describe the bug
When the server respond with an error (like 500 for example), data should be null and error should be the error.

Expected behavior
When calling fetch, response should be check with response.ok, if response is not ok, error should be set, not data.

Window is not defined

When used with Next.js and trying to reload a page from bowser, get following error:

ReferenceError: window is not defined
    at /Users/user/workspace/project1/node_modules/use-http/src/useFetch.ts:61:30
    at useFetch (/Users/user/workspace/project1/node_modules/use-http/src/useFetch.ts:129:27)
    at Reports (/Users/user/workspace/project1/build/server/static/development/pages/reports.js:685:76)
    at processChild (/Users/user/workspace/project1/node_modules/react-dom/cjs/react-dom-server.node.development.js:2888:14)
    at resolve (/Users/user/workspace/project1/node_modules/react-dom/cjs/react-dom-server.node.development.js:2812:5)
    at ReactDOMServerRenderer.render (/Users/user/workspace/project1/node_modules/react-dom/cjs/react-dom-server.node.development.js:3202:22)
    at ReactDOMServerRenderer.read (/Users/user/workspace/project1/node_modules/react-dom/cjs/react-dom-server.node.development.js:3161:29)
    at renderToString (/Users/user/workspace/project1/node_modules/react-dom/cjs/react-dom-server.node.development.js:3646:27)
    at render (/Users/user/workspace/project1/node_modules/next-server/dist/server/render.js:86:16)
    at renderPage (/Users/user/workspace/project1/node_modules/next-server/dist/server/render.js:211:20)
    at Function.value (/Users/user/workspace/project1/build/server/static/development/pages/_document.js:555:41)
    at Object.loadGetInitialProps (/Users/user/workspace/project1/node_modules/next-server/dist/lib/utils.js:42:35)
    at Object.renderToHTML (/Users/user/workspace/project1/node_modules/next-server/dist/server/render.js:218:36)

How to fetch multiple URLs (non-constant number) at once?

Say I need to fetch a list of remote resources, while the length on the list depends on the current props. (How) can I achieve that with use-http? As far as I understand the documentation of react, the number of hooks in a component needs to be constant.

An in-range update of use-ssr is breaking the build 🚨

The dependency use-ssr was updated from 1.0.18 to 1.0.19.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

use-ssr is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

What would be the way to pass cookies with requests?

Is there a way to pass cookies with requests?
In axios, for example, one would do someting like this:

axios.create({
  withCredentials: true
})

And, using Fetch Api:

fetch('https://example.com', {
  credentials: 'include'  
})

Is there an option to send and recieve cookies to/from the server?

Uncaught Error in create-react-app

package.json:
"dependencies": {
"node-sass": "^4.12.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1",
"use-http": "^0.1.62"
},

App.js:
import {useGet} from 'use-http';,

console panel:
Uncaught Error: Cannot find module 'idempotent-babel-polyfill'
at webpackEmptyContext (dist sync:2)
at f (index.js:14)
at p (index.js:29)
at Object.push../node_modules/use-http/dist/index.js.parcelRequire.RaX2 (index.js:62)
at f (index.js:23)
at p (index.js:29)
at Object.push../node_modules/use-http/dist/index.js.parcelRequire.Focm../useFetch (index.js:513)
at f (index.js:23)
at push../node_modules/use-http/dist/index.js.parcelRequire.RaX2 (index.js:42)
at Object../node_modules/use-http/dist/index.js (index.js:56)
at webpack_require (bootstrap:781)
at fn (bootstrap:149)
at Module../src/App.js (App.js:4)
at webpack_require (bootstrap:781)
at fn (bootstrap:149)
at Module../src/index.js (App.scss?2f5e:45)
at webpack_require (bootstrap:781)
at fn (bootstrap:149)
at Object.0 (serviceWorker.js:135)
at webpack_require (bootstrap:781)
at checkDeferredModules (bootstrap:45)
at Array.webpackJsonpCallback [as push] (bootstrap:32)
at main.chunk.js:1

SSR Not working

I'm using version 0.1.68 but nothing is working. This is my codes:

import React from "react";
import useFetch from "use-http";

export default () => {
  const stories = useFetch(
    "https://hacker-news.firebaseio.com/v0/topstories.json",
    {
      onMount: true
    }
  );

  if (stories.error) return "Error";
  if (stories.loading) return "Loading";

  return <pre>{JSON.stringify(stories.data, null, 2)}</pre>;
};

Create-react-app linting question

This isn't technically an issue with use-http (which I'm currently in love with for everything fetch-related) but in create-react-app, following the basic usage example create-react-app default linting shows the error React Hook useEffect has a missing dependency: 'initializeTodos'. Either include it or remove the dependency array react-hooks/exhaustive-deps. If I follow this guidance, the error changes to The 'initializeTodos' function makes the dependencies of useEffect Hook (at line 53) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'initializeTodos' definition into its own useCallback() Hook react-hooks/exhaustive-deps.

The example seems to work exactly as expected without adding to the dependency array, but I'm wondering if this breaks best practices. Is the linter being too fussy? Or should I be modifying the example to follow this rule?

Apologies if this shouldn't be an issue, I can re-post to stackoverflow.com if recommended. Many thanks for this great module!

Trigger new request on url change

Currently I can't figure out a short and simple way to fetch data onMount and when the url changes.

The code below only triggers on mount, and looking at the docs (and source code) it doesn't seem like useGet takes a dependency array (like useEffect does) that indicates when the effect should execute again, is there some other option to get it re-run on url change as well as mount? Am I missing some other trivial way of accomplishing this?

import {useGet} from 'use-http'

function Todos(props) {
  const options = {
    onMount: true // will fire on componentDidMount
  }

  const todos = useFetch('https://example.com/todos/' + props.someID, options)

  return (
    <>
      {request.error && 'Error!'}
      {request.loading && 'Loading...'}
      {(todos.data || []).length > 0 && todos.data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}

An in-range update of eslint-plugin-jest is breaking the build 🚨

The devDependency eslint-plugin-jest was updated from 22.9.0 to 22.10.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

eslint-plugin-jest is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ci/circleci: test: Your tests failed on CircleCI (Details).

Release Notes for v22.10.0

22.10.0 (2019-07-17)

Features

Commits

The new version differs by 7 commits.

  • 28bd1dc feat(rules): adds no-if rule (#293)
  • 7ebdc0e chore: enforce import destructure order
  • 31c7cef chore: convert to import/export (#302)
  • 9f858cb chore: delete tests instead of ignoring them with babel
  • c595ba0 chore: do not include tests in published tarball
  • 4b4eb78 chore: fix lint error in md file
  • d3ea720 chore(docs): fix typo (#304)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

is the graphql support worth maintaining

I hope this question does not come across as rude, it really is not meant to be.

I did a spike with GraphQL a while ago but I'm sort of curious where you see use-http fit into the ecosystem.

If I am using apollo or urql, would I not just use that?

Please feel free to shoot me down. This is just me being curious.

Feature Requests and Syntax Ideas

Abort

I've been toying around with different ways to implement this.

Ideas:

We can abort multiple http requests at once

const controller = new AbortController()
const todos = useFetch('http://example.com/todos', { controller })
todos.abort() // 🚫will error
const messages = useFetch('http://example.com/messages', { controller })
messages.abort() // 🚫will error
controller.abort()  // ✅

Individual Aborts

const todos = useFetch('http://example.com/todos')

todos.abort()

const messages = useFetch('http://example.com/messages')

messages.abort()

Typeahead usage
these are potential options for, if request1 is pending, and request2 is made, cancel request1. Or if they were set to 2, make 2 requests before canceling prior ones. Not sure the best way to handle this.

const todos = useFetch('http://example.com/todos', {
    deferred: 1,
    maxRequests: 1,
    limit: 1,
    typeahead: true,
    abort: 1,
})

function onChange(e) {
  todos.get(`?q=${e.target.value}`)
}

todos.data.map(todo => /* example */)

Syntax

I'm starting to like this syntax better, but what do you all think?

const todos = useFetch('http://example.com/todos')

todos.data
todos.get()
todos.post({
  name: 'cool'
})
todos.abort()
todos.loading

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.