npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-ufo

v0.0.7

Published

🛸 UFO - Use fetch orderly - A small collection of react hooks to help you handle data fetching with no fuss 🛸

Downloads

458

Readme

Actions Status

Introduction

When updating a UI with data retrieved form a remote server a lot of things can go wrong

  • you will need to handle loading and error state
  • you might have two or more requests depending on each others
  • you might want to abort pending requests in certain conditions
  • you might have to handle race conditions

At a first sight these problem seems no big deal but things get out of control quite easily.

Taking advantage of react hooks react-ufo helps you dealing with all this complexity.

Installation

npm install --save react-ufo

import {useFetcher} from "react-ufo"

How to use

Basic usage

useFetcher handles the state of a request for you and much more.

The minimal usage of useFetcher looks like the following:

const [callback, [loading, error, data]] = useFetcher(fetcher)

A fetcher function is a normal function that fetches some data and returns a promise.

Here an example of fetcher function:

const getTodo = async (id) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/" + id);
  return response.json();
};

When you want your request to start, all you need to do is to invoke callback, after doing so, loading, error, and data will be updated in accordance with the status of your request.

Any argument you pass to callback will be passed to your fetcher.

Note: Do not create a new fetcher function on every render, useFetcher will create a new callback anytime a new fetcher instance is received. In case your fetcher depends on props simply pass them to callback and your fetcher will receive them.

Here a basic example showing how to use useFetcher in an event callback such as onClick Edit 1basicFetchInEventCallbackExample

Fetching on mount/update

By default, before a request is started, useFetcher will return loading=false, error=null, data=null.

Sometimes you might want your initial request state to be different.

One example is if you plan to request your data on the component mount/update, in this case you might want your initial request state to have loading=true.

useFetcher can receive a second argument indicating the initial state before your request starts.

Here how you override the default loading state to be true

const [callback, [loading, error, data]] = useFetcher(fetcher, {loading:true})

Now if you want your request to start on mount all you need to do is

useEffect(()=>{
  callback()
},[callback])

You don't have to worry about passing callback as a dependency of useEffect, callback will only change if your fetcher changes.

Fetching on mount/update with props

Sometimes a fetcher might need some data in order to retrieve data, for example the getTodo presented earlier needs an id argument.

Assuming id is a prop of your component all you need to do is

useEffect(()=>{
  callback(id)
},[id,callback])

this ensure that your fetcher will be invoked on mount and anytime id updates, which is usually what you want.

Here a basic example showing how to use useFetcher during mount/update Edit 2basicFetchOnMountAndUpdateExample

Ignoring a pending request

If your component is unmounted while one of its requests is still pending useFetcher will take care of ignoring its result avoiding an attempt to perform a setState on an unmounted component.

Sometimes you might want to ignore the result of a request for other reasons too.

callback.ignore() can be invoked if you need to ignore the result of a pending request.

If a pending request is marked as ignored loading, error and data will not be updated once the request is completed.

Aborting a pending request

callback.abort() can be invoked anytime you want to abort a pending request.

Unfortunately in order for callback.abort() to work properly there is some little more wiring that you'll need to do.

useFetcher will take care of passing an abort signal to your fetcher as its last argument.

In order for callback.abort() to work you'll need to pass the abort signal to your fetch API.

Here an example showing how to enable fetch abortion on the getTodo fetcher presented earlier

const getTodo = async (id, signal) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/" + id, {signal});
  return response.json();
};

If your fetcher is not passing the abort signal to fetch API invoking callback.abort() will not abort the request but the request will still be marked as ignored.

If a request is marked as ignored loading, error and data will not be updated once the request is completed.

Here an example showing how to abort a request Edit 3basicAbortFetchExample

Aborting a pending request is quite easy when using fetch API but it can also be achieved if you are using other libraries such as axios

If you are wondering how to abort a request started by axios instead of fetch API you can find an example here Edit abortRequestIfUsingAxiosExample

Cascading fetches

Sometimes 2 requests depend on each other.

Let's say that you fetched a todo object containing a userId field and you want to use userId to fetch a user object.

Here how you can handle this use case with useFetcher:


...

const [fetchTodo, [loadingTodo, todoError, todo]] = useFetcher(todoFetcher)
const [fetchUser, [loadingUser, userError, user]] = useFetcher(userFetcher)

useEffect(()=>{
  fetchTodo(todoId).then((todo)=>{
    fetchUser(todo.userId)
  })
},[todoId, fetchTodo, fetchUser])

...

Here the full example showing this use case Edit 4cascadingFetchesExample

Keeping state between fetches

By default useFetcher erases the data of a request anytime a new one is started.

Most of the times this is what you want but there are cases where you want to keep the data visible to the user until new data are retrieved.

If you need to keep data between fetches you can simply use useState from react.

Here an example showing how to keep data while multiple request are pending:

const [data, setData] = useState()
const [callback, [loading, error, _data]] = useFetcher(fetcher)

...

const myEventCallback = ()=>{
  callback(1).then((data)=>{
    setData(data)
    callback(2).then((data)=>{
        setData(data)
    })
  })
} 

in the previous example _data is set to null anytime a new request is started while data is only valued when a request is completed.

Debouncing requests

Here an example showing one simple way to debounce requests Edit 5debounceFetchExample

Mutating state

Sometimes you might want to change your request state manually.

One common scenario when this can happen is if your user decides to ignore and remove a request error message displayed on the screen.

useFetcher provides you setLoading, setError, setData and setRequestState for you to handle these use cases.

Here the full signature of useFetcher:

const [callback, [loading, error, data], setRequestState] = useFetcher(fetcher)
const [setLoading, setError, setData] = setRequestState

setLoading, setError, setData and setRequestState should be self explanatory, they work exactly like the setState in const [state, setState] = useState()

Putting all together

Here an example showing how useFetcher can be used to implement a simple CRUD application Edit 6crudExample

useFetcher API

Here the full useFetcher API

const initialRequestState = {loading:false, error:null, data:false} //these are the default values if initialRequestState is not provided
const [callback, requestState, setRequestState] = useFetcher(fetcher, initialRequestState)
const [loading, error, data] = requestState
const [setLoading, setError, setData] = setRequestState

What exactly is useFetcher returning?

useFetcher returns a result object shaped as follow:

{
  callback,
  requestState: {
    loading,
    error,
    data
  },
  setRequestState: {
    setLoading,
    setError,
    setData
  }
}

result, requestState and setRequestState are also iterable, therefore, if you find it convenient for renaming, you can destructure them into an array as follow:

const [callback, [loading, error, data], [setLoading, setError, setData]] = result

When destructuring into an array you obviously need to rely on the order we specified for each key, therefore, in case you don't want to extract all the fields from result, you might need to write something like the following:

const [callback, [loading, , data], [, setError]] = result

Because result is an object, accessing its fields by key (e.g const data = result.requestState.data) is going to work as expected too.

Because result is an object, doing object destructuring is going to work as expected too.

Note that even though setRequestState contains setLoading, setError, setData it is a function and can be used to update loading, error and data in a single render.

Note: Even though result, requestState and setRequestState are iterable they are not arrays, therefore something like result[0] or result.requestState[0] is not going to work.

Examples

  1. basic fetch in event callback Edit 1basicFetchInEventCallbackExample
  2. basic fetch on mount/update Edit 2basicFetchOnMountAndUpdateExample
  3. aborting a pending request Edit 3basicAbortFetchExample
  4. handling requests depending on each others Edit 4cascadingFetchesExample
  5. debouncing requests Edit 5debounceFetchExample
  6. simple CRUD application Edit 6crudExample
  7. aborting a pending request started with axios Edit abortRequestIfUsingAxiosExample

Package versioning

Breaking changes might be made between 0.x.x versions. Starting from version 1.0.0 every breaking changes will result in a major version update. The changelog will give you details about every change between versions.

Dependencies

This package has zero dependencies but in order to support fetches abortion you will need AbortController (or a polyfill such as abortcontroller-polyfill) in your environment