How to handle async side effects in 2019
Tags: react, javascript
Published: 2019-10-29 14:20:36

---
title: How to handle async side effects in 2019
published: true
date: 2019-10-29 13:00:35 UTC
tags: react,javascript
canonical_url: https://blog.logrocket.com/how-to-handle-async-side-effects-in-2019/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/cxa2mhjfi31h2cotrwhp.png
---
**Written by [Peter Ekene Eze](https://blog.logrocket.com/author/peterekeneeze/)**✏️

Handling async operations has been a major concern for developers in the React ecosystem.

There’s been a bunch of different approaches to handling async operations, including Redux-Saga, but in this article we’ll be focusing on what I think is currently the best approach: using `react-async`.

We’ll also make comparisons between the `react-async` library and other existing methods of handling asynchronous side effects in React.

### What is React Async?

React Async is a promise-based tool that lets you handle promises and fetch data declaratively.

It makes it easy to handle asynchronous UI states without assumptions about the shape of your data or the type of request.

React Async consists of a React component and several Hooks. You can use it with fetch, Axios, GraphQL, and other data fetching libraries.

React Async relies on using a declarative syntax, JSX, and native promises to resolve data closer to where you need them in your code (for example, on the component level), unlike other systems like Redux where resolving data happens at a higher level in your application using things like actions and reducers.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### React Async Usage

To use React Async like in the example below, we will import `useAsync` from `react-async`

Then we can create our asynchronous function, which receives a signal as a parameter. The signal is our AbortController API, which provides a way for us to cancel the fetch call we make if there’s ever a need to do so.

In our component, we call `useAsync` and pass on our asynchronous function.

Calling `useAsync` returns an object that we can de-structure into three important values: data, error, and isPending.

These values tell us about the state of our asynchronous function — whether it’s still pending, errored out, or successful.

We can use each of these values to render an appropriate UI for the user:

```jsx
import { useAsync } from "react-async"
// You can use async/await or any function that returns a Promise
const asyncFn = async ({ signal }) => {
  const res = await fetch(`/api/users`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}
const MyComponent = () => {
  const { data, error, isPending } = useAsync({ promiseFn: asyncFn })
  if (isPending) return "Loading..."
  if (error) return `Something went wrong: ${error.message}`
  if (data)
    <ul>
      {data.users.map(user => <li>{user.name}</li>)}
    </ul>
)
return null
```

There are a few documented ways of using React-Async:

- As a Hook
- With `useFetch`
- As a component
- As a factory
- With helper components
- As static properties of

I’ll be briefly touching the first three methods just to give you an idea of these implementations, but feel free to reference the [official usage guide](https://docs.react-async.com/getting-started/usage) to get an in-depth view of each of the methods.

### React Async as a Hook

React-Async provides a Hook called `useAsync`. Within your component, you can call this Hook like so:

```jsx
import { useAsync } from "react-async";

const MyComponent = () => {
  const { data, error, isPending } = useAsync({ promiseFn: loadPlayer, playerId: 1 })
  //...
};
```

### React Async with `useFetch`

With `useFetch`, you are creating an asynchronous fetch function which can be run at a later time in your component:

```jsx
import { useFetch } from "react-async"
const MyComponent = () => {
  const headers = { Accept: "application/json" }
  const { data, error, isPending, run } = useFetch("/api/example", { headers }, options)
  // You can call "handleClick" later
  function handleClick() {
    run()
  }
<button onClick={handleClick}>Load</button>
}
```

### React Async as a component

Here’s where React Async really shines with JSX:

```jsx
import Async from "react-async"
const MyComponent = () => (
  <Async promiseFn={load}>
    {
      ({ data, error, isPending }) => {
        if (isPending) return "Loading..."
        if (error) return `Something went wrong: ${error.message}`
        if (data)
          return (<div> { JSON.stringify(data, null, 2) }</div>)
        return null
      }
    }
  </Async>
)
```

You have to pass a function to the `Async` component as a child.

As you can see, this function will evaluate different node values based on the state of the asynchronous function we provided as props to `Async`.

### React Async vs `useEffect`

`useEffect` in combination with Async/Await isn’t quite as convenient as React Async, especially when you start thinking of race conditions, handling clean-ups, and cancelling pending async operations.

React Async handles all these things for you in a very efficient manner.

Let’s take a look at a typical example of handling race conditions with `useEffect` and Async/Await:

```jsx
const [usersList, updateUsersList] = useState();
useEffect(() => {
  const runEffect = async () => {
    const data = await fetchUsersList(filter);
    updateUsersList(data);
  };
  runEffect();
}, [updateUsersList, filter]);
```

In the case above, if for any reason we have to call the `useEffect` twice and the second call to `fetchUsersList` resolves before the first, we’d have an outdated “updated” list.

You can fix this by adding a way to prevent the `updateUsersList` call from happening when you deem it necessary, however, such methods might not scale well with multiple `await` expressions.

On the other hand, you don’t have to wory about cancelling unresolved requests or handling proper race conditions when you use React Async because React already handles that for you:

```jsx
import { useAsync } from "react-async"
// You can use async/await or any function that returns a Promise
const fetchUsersList = async ({ signal }) => {
  const res = await fetch(`/api/users`, { signal })
  if (!res.ok) throw new Error(res.statusText)
  return res.json()
}
const filteredUsers = (users) => {
  // Filter users ...
}
const MyComponent = () => {
  const { data, error, isPending } = useAsync({ promiseFn: fetchUsersList})
  if (isPending) return "Loading..."
  if (error) return `Something went wrong: ${error.message}`
  if (data)
  <ul>
    { filteredUsers(data.users).map(user => <li>{user.name}</li>) }
  </ul>
)
return null
```

In the above code snippet, any time we call `fetchUsersList`, we would be re-rendering the `MyComponent` component, which means we’ll always have the state we expect.

Also, React Async does a clean up internally for us and cancels unresolved promises using the `AbortController` API (i.e the `signal` variable being passed to `fetchUsersList` function,) so we don’t have to worry about race conditions and cancelling unresolved promises that we no longer need.

If your application is really basic and adding a 14kb library to handle async operations doesn’t make sense, then you could settle for a slightly more advanced implementation of `useEffect`.

In my opinion, React Async is already pretty light-weight and has lots of great benefits in addition to being well-tested.

So unless the gains you get from saving 14kb is crucial, you might want to use React Async.

### React Async vs Redux-Saga

`Redux-Saga` is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures: [redux-saga.js.org.](https://redux-saga.js.org/)

Redux-Saga requires a lot more steps to get started than React Async.

That’s because it’s a Redux middleware, which means you have to set up Redux for it.

The idea of Redux is to have a centralized state for all or major parts of your application. That way, you can update your state through dispatching `actions`. For instance:

```jsx
const Counter = ({ value }) =>
  <div>
    <button onClick={() => store.dispatch({type: 'INCREMENT_ASYNC'})}>
      Increment after 1 second
    </button>
    <hr />
    <div>
      Clicked: {value} times
    </div>
  </div>
```

Redux-Saga helps you make network calls or perform other asynchronous side effects by relying on “ES6 Generators”:

```jsx
function* incrementAsync() {
  yield delay(1000)
  yield put({ type: 'INCREMENT' })
}
```

As you can see, where you do your side effects is far removed from your components. You trigger updates by dispatching actions within your component. Then, the updated state comes in through your props.

Pretty standard stuff, but very different and less intuitive than what React Async gives you.

### Conclusion

- With React Async, you don’t need to assume how your data is going to look as you have to do with Redux. It’s just like how you’d typically use Promises.
- With React Async, you resolve your data closer to where you need it, making it clearer to understand what’s going on.

You don’t have to understand a fairly complex structure with constructs like reducers and actions — you utilize what you already know and use within your components – JSX, Promises, and Hooks.
* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/how-to-handle-async-side-effects-in-2019/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [How to handle async side effects in 2019](https://blog.logrocket.com/how-to-handle-async-side-effects-in-2019/) appeared first on [LogRocket Blog](https://blog.logrocket.com).