Skip to content

Releases: ava/use-http

Removal of "path" and "url" fields

23 Apr 02:29
8c8937d
Compare
Choose a tag to compare
  1. this removes the path and url fields in exchange for the 1st argument handling both. #247
    🚨 BREAKING 🚨
useFetch('/path')
// AND
useFetch('https://overwrite-global-url.com')
  1. Fixes issue when overwriting global options, it changes for every instance of useFetch. #250

  2. fixes the pagination issue here #237 but might not fix the loading bug.

  3. small bug fix with responseType

Scalable Interceptor Arguments, responseType option

17 Apr 02:17
Compare
Choose a tag to compare

Scalable Interceptor Arguments

🚨🚨THIS RELEASE CONTAINS BREAKING CHANGES!!! 🚨🚨
Due potential future scalability issues such as adding new items to pass to the interceptors, we're changing it to an object destructuring syntax instead of multiple arguments.

useFetch({
  interceptors: {
    request: async (options, url, path, route) => {},
    response: async (response) => {}
  }
}

will now be

useFetch({
  interceptors: {
    request: async ({ options, url, path, route }) => {}, // <- notice object destructuring
    response: async ({ response }) => {} // <- notice object destructuring
  }
}

This provides a few benefits.

  1. we can now add new options to these callbacks without having breaking changes
  2. we can pull out these options in whatever order so we don't have unused variables

For example, let's say in the future we want to do

useFetch({
  interceptors: {
    request: async ({ options, route, attempt }) => {
      // now we can add a new field `attempt`
      // and we can just grab `route` without `url` and `path`
    },
    response: async ({ response, attempt }) => {
      // now we can add a new field `attempt`
    }
  }
}

responseType option

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.
Defaults to: ['json', 'text', 'blob', 'readableStream']

useFetch({
  // 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'],
})

Version 1.0

14 Apr 20:06
35d99b1
Compare
Choose a tag to compare

Finally Version 1.0

Lots of upgrades in this version. This new major release is v1.0.2 on npm.

Updates

  1. Retry Functionality. retries, retryDelay, and retryOn
  2. Suspense(experimental) Support. suspense
  3. Persistent Caching. persist
  4. Added cache to return of useFetch for better cache debugging and to clear the cache for instance when a user signs out.
  5. Fixed infinite loop/extra rendering issues when passing useFetch variables to useEffect dependencies #228 #185
  6. Fixed response interceptor not firing when results loaded from cache #235
  7. Can now have [] and '' as body of http request. Caveat, if posting a string as the body, must have a route post('/', 'your body')
  8. Added async support for interceptors.response #214
  9. Remove content-type: application/json when posting formData #213
  10. Promise.all fixes #211
  11. Fixed cannot perform update on unmounted component bug #207
  12. Should now work in TypeScript apps made using parcel #202
  13. Lot's of videos added!

Pagination + Moving to dependency array for onMount + onUpdate

19 Nov 07:36
071a6b9
Compare
Choose a tag to compare

🚨⚠️ This release has breaking changes! ⚠️🚨

  • removed onMount and onUpdate in exchange for accepting a dependency array as the last argument of useFetch
  • added onNewData to be used for merging new fetched data with current data for pagination

onMount AND onUpdate Pagination example

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

const Todos = () => {
  const [page, setPage] = useState(1)

  const { data, loading } = useFetch({
    path: `/todos?page=${page}&amountPerPage=15`,
    onNewData: (currTodos, newTodos) => [...currTodos, ...newTodos], // appends newly fetched todos
    data: []
  }, [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>
)

onMount only example

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

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

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

const App = () => (
  <Provider url='https://example.com'>
    <Todos />
  </Provider>
)

onUpdate only example:

If for some reason you need to only do onUpdate without onMount, you will have to use managed state for this.

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

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

  const mounted = useRef(false)
  useEffect(() => {
    if (mounted.current) {
      // onUpdate only
      get()
    }
    mounted.current = true
  })

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

const App = () => (
  <Provider url='https://example.com'>
    <Todos />
  </Provider>
)

Support for a Provider, useMutation, and useQuery

20 Jun 07:39
b912f72
Compare
Choose a tag to compare

Provider, useMutation, and useQuery

Provider using the GraphQL useMutation and useQuery

The Provider allows us to set a default url, options (such as headers) and so on.
There is array and object destructuring for useMutation and useQuery.

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

This uses array destructuring, but it can also use object destructuring. The useMutation and useQuery are very similar to the usePost's array and object destructuring.

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

Fetch more todos

function FetchComponent() {
  const request = useFetch('/todos')
  
  return (
    <>
      <button onClick={request.get}>Get Todos</button>
      {request.loading ? 'Loading...' : <pre>{request.data}</pre>}
    </>
  )
}

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

Abort Functionality

28 Apr 00:12
323fa78
Compare
Choose a tag to compare

Abort

In this release, we've added abort functionality. Now you can call abort on any http request and it will cancel it. We decided not to allow aborting multiple requests at once. If the community decides that this is a feature we need to add, we will add it. If you want that, please comment on the Feature request and syntax ideas issue.

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

Fetching on-demand vs only in "componentDidMount"

20 Apr 08:38
Compare
Choose a tag to compare

Changing the original behavior from only fetching on mount to whenever you want.
Previous behavior:

import useFetch from 'use-http'

function App() {
  // add whatever other options you would add to `fetch` such as headers
  const options = {
    method: 'POST',
    body: {}, // whatever data you want to send
  }
  
  var [data, loading, error] = useFetch('https://example.com', options)
  
  // want to use object destructuring? You can do that too
  var { data, loading, error } = useFetch('https://example.com', options)
  
  if (error) return 'Error!'
  if (loading) return 'Loading!'
  
  return (
    <code>
      <pre>{data}</pre>
    </code>
  )
}

this would only work when the component first rendered. Now we can do

import useFetch from 'use-http'

function App() {
  // add whatever other options you would add to `fetch` such as headers
  const options = {}
  
  var [data, loading, error, request] = useFetch('https://example.com', options)
  
  // want to use object destructuring? You can do that too
  var { data, loading, error, request, get, post, patch, put, del } = useFetch('https://example.com')
  
  const postData = () => {
    post({
      no: 'way',
    })
    // OR
   request.post({
      no: 'way',
    })
  }

  if (error) return 'Error!'
  if (loading) return 'Loading!'
  
  return (
    <>
      <button onClick={postData}>Post Some Data</button>
      <code>
        <pre>{data}</pre>
      </code>
    </>
  )
}

There's also support for

var [data, loading, error, request] = useFetch({
  onMount: true, // will fire on componentDidMount
  baseUrl: 'https://example.com'
})

const handleClick = () => {
  request.post('/todos', {
    id: 'someID',
    text: 'this is what my todo is'
  })
}

And don't forget

var { data, loading, error, patch } = usePatch({
  url: 'https://example.com',
  headers: {
    'Content-type': 'application/json; charset=UTF-8'
  }
})