Technology Blog

Look deep into latest news and innovations happening in the Tech industry with our highly informational blog.

Don’t use async Redux middleware

hkis

 
Redux makes synchronous state updates explicit and reasonable, and makes visible the contract between React and the application: UI is a function of state, and it’s for this reason we’ll continue to use it.

But asynchronous effects are a whole other matter. we have variously used the middleware libraries Redux Thunk, Redux Loop and Redux Saga. Part-way through reading the Redux Observable documentation we started a clickbait Reddit thread and got pointed towards a better approach.

Before we go into that approach, here’s where we see problems with the leading async middleware libraries.

redux_as_middleware

The problem with thunks

Using Redux Thunk mean having some of your action creators return async functions (thunks) that have access to dispatch(), instead of plain objects.

Consider a typical async scenario — you want to grab something from an API and dispatch the response to the store. But you may also want to do other things with that API response — pop up a notification, say, or record some analytics to a 3rd party API.

If you incorporate those effects into your thunk, it will have some undesirable characteristics:

  • it is now tightly bound to areas of your state it was previously unconcerned with
  • your previously simple function is more bloated
  • some of your async business logic, like analytics, gets spaghettified throughout unrelated thunks
  • previously pure-object-returning actions now have to become thunks just to fire off async side effects

Not great. The problem with thunks is that they’re not powerful enough, not on their own.

The problem with sagas

Redux Saga allows you to define generator functions (sagas) that grab actions as they enter the store, perform whatever asynchronous actions they need, and dispatch() resulting actions. They can also select from the store.

This removes most of the problems exhibited above. By creating a showNotificationAfterApiResponse saga that grabs the response action and updates some UI state, and a separate analyticsSaga that grabs disparate actions to record events to your 3rd party analytics API, you can keep your original fetch-and-dispatch effect untouched.

However, sagas can do more than that. Being based around generators, they can pause their execution until certain conditions are met. They can also grab actions under certain conditions (just one action ever, or just the latest action, or take every action always). And this means sagas exhibit some different undesirable characteristics:

Generators are inherently stateful, so the effects that will be executed by your application do not solely depend on Redux state+action, but on the current state of this saga and any other saga that may get called by its operation
Generators are imperative, meaning anything other than the most simple cases can be challenging to grok
Generators are quite unusual syntactically, increasing the barrier to entry for new developers

In search of a better approach

It expanded on the Redux formula(state+action=new state) to include asynchronous side effects (state+action=new state+side effects). The library is avoided by a lot of developers as it seems to affect the purity of Redux reducers, but that’s a bit misleading: it doesn’t actually run the effects, it just returns what they will be.

As it turned out, we abandoned Redux Loop for other reasons — it does not play nice with other middleware (such as the wonderful Redux Persist), its syntax is fairly clunky, its uptake is low and falling — but the looped effect code had some properties that we found attractive:

It could take actions like sagas or epics without the async pollution experienced by thunks
It colocated async and synchronous effects, making it clear what responsibility each slice had for the side effects that were run
By keeping the switch (action.type) mechanic, it resembled vanilla Redux, and so was more grokkable than generators or RxJS streams

“Why not write your own custom middleware?”

This was the sentence we read that forever changed the way I wrote async Redux. we hope to use it to convince you to change yours, too.

Vanilla Redux consists of 3 elements of boilerplate for each slice of state:

  • Action types
  • Action creators
  • Reducer

Our approach adds a fourth:

  • Custom middleware

Not every slice needs all four, though we find most do. Some slices just need the vanilla synchronous 3 elements, some (like analytics) only need the middleware element, and we have exotic cases that didn’t need state or a reducer, but just actions and the middleware to dispatch them.

In all cases, the pattern we use for custom middleware is this: alongside any userActions and a userReducer, we’ll add a userMiddleware

const userMiddleware = ({ getState, dispatch }) => next => async action => {
  const result = next(action);
  switch (action.type) {
    case SOME_ACTION:
      const asyncResult = await somethingAsync();
      dispatch(anotherAction(asyncResult));
    break;
    case SOME_OTHER_ACTION:
      const { slice: { stateVariable } } = getState();
      await someProcess(stateVariable);
    break;
  }
  return Promise.resolve(result);
}

Note how, when actions go in the top of the async function, they immediately get next(action)-ed (passing them along to the next middleware or the destination store), and then there’s a switch (action.type) with cases that perform the side effects before the promised result is resolved (which allows your dispatch()es to be awaited).

And this is what we love about this pattern: the asynchronous middleware closely resembles a synchronous reducer. Just as the reducer is pure in that a given action on a given state will always produces the same resultant state, the middleware has its own purity: a given action on a given state will always run the same effects.

This makes async effects easy to understand. It has given up the power of the saga (it now can only take every action of a type) but in doing so has simplified it and made tracking your side effects obvious.

For more Information and to build a website using React JS, Hire React Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.To develop your custom website using React JS, please visit our technology page.

Content Source:

  1. medium.com