Redux Async Utils

Give to redux some async useful utils to being more smart

Actual State

Actually, if we have more async actions in our redux application, we can use these libs:

 

  • redux-thunk: make actions async
  • some middleware for fetch api
  • ... and other lib like redux-async

 

All of these contribute to transform the core flow of redux (that is sync) to an async workflow.

What we miss

Developing redux application, it might be possible you have to download some data or doing some other async operations.

 

So, it is possible with the thunk middleware that help you to dispatch actions inside other actions.

 

But... Let me introduce some code for a download action.

What we miss

// action type
const DOWNLOAD_TODOS_LOADING = 'DOWNLOAD_TODOS_LOADING';
const DOWNLOAD_TODOS_SUCCESS = 'DOWNLOAD_TODOS_SUCCESS';
const DOWNLOAD_TODOS_ERROR = 'DOWNLOAD_TODOS_ERROR';

// action
function downloadTodosActionCreator( ) {
    return ( dispatch, getState ) => {
      dispatch( { type: DOWNLOAD_TODOS_LOADING } );

      // some fetch
      fetchTodos( )
        .then( ( result ) => {
          dispatch( { type: DOWNLOAD_TODOS_SUCCESS, payload: result } );
        } )
        .catch( ( e ) => {
          dispatch( { type: DOWNLOAD_TODOS_ERROR, error: e } );
        } );
    };
}

What we miss

In the previous example of code we have an async action that download todos using 3 actions:

  • DOWNLOAD_TODOS_LOADING
  • DOWNLOAD_TODOS_SUCCESS
  • DOWNLOAD_TODOS_ERROR

 

We have too action type to do only one thing.

Also reducers has a lot of duplicated code, to manage the loading state in the store, errors or success with data, for each async action.

Available solution

We could create some store enhancer and middleware to avoid rewriting all of these code lines.

 

So, redux-await seems to do something like this.

Using a middleware that run a Promise when you insert the AWAIT_MARKER into an action. You have also to insert the promise inside action's payload.

Available solution


import { AWAIT_MARKER } from 'redux-await';
export const getTodos = () => ({
  type: GET_TODOS,
  AWAIT_MARKER,
  payload: {
    loadedTodos: api.getTodos(), // returns promise 
  },
});
export const addTodo = todo => ({
  type: ADD_TODO,
  AWAIT_MARKER,
  payload: {
    savedTodo: api.saveTodo(todo), // returns promise 
  },
});

Available solution

import { connect } from 'redux-await';

class Container extends Component {
  render() {
    const { todos, statuses, errors } = this.props;
 
    // old code 
    //return <div> 
    //  <MyList data={todos} /> 
    //</div>; 
 
    // new code 
    return <div>
      { statuses.loadedTodos === 'pending' && <div>Loading...</div> }
      { statuses.loadedTodos === 'success' && <MyList data={loadedTodos} /> }
      { statuses.loadedTodos.status === 'failure' && <div>Oops: {errors.loadedTodos.message}</div> }
      { statuses.savedTodo === 'pending' && <div>Saving new savedTodo</div> }
      { statuses.savedTodo === 'failure' && <div>There was an error saving</div> }
    </div>;
  }
}

export default connect(state => state.todos)(Container)

Available solution

Now we have the ability to track all the async actions without writing all that boilerplate code.

Good idea!!

 

However, we have some trouble with this:

  • we have to pay attention to the payload promise name, we can't use same name in different actions
  • we don't know when all the async actions that our component is waiting for are done
  • we can't do something like Promise.all to concatenate more async operation

What I suggest

  • let developer have more control on the dispatch operation
  • let developer have more control on the async operation itself
  • avoid using more names than needs
  • let other software part know about the async state of the actions
  • let know when all the needed async actions are done (useful for component and server-side rendering)
  • consider performance inside mapStateToProps function, we wouldn't make the connector re-render all the component that listen to the async state of all the actions
  • manage optional async function
  • avoid manage async actions state in the same store parts of the data, clean data is better, you might have other actions that manage the same data
  • easy management, easy install and less information to be known, in terms of API and names to manage conflict in the store

My purpose

Text

Redux Async Utils

By Luca Colonnello

Redux Async Utils

Give to redux some async useful utils to being more smart

  • 585