Async React Native Actions with ES2017

React Native

React Native seems to have taken the mobile hybrid world by storm by breaking the confines of the Web View.

Naturally, a React Native would interact with a server to fetch and update data. Such interaction is inherently asynchronous.

Async React Native Actions

React Native actions are a natural place for asynchronous interactions.

One can program such actions based on fetch using promises with then. Like,

export const loadData = (params) => {

  const req = fetch(url);

  return {

    type: LOAD_DATA,

    payload: req.then(response => response.json())

  };

};

The drawback of this approach is that it keeps you thinking explicitly about the asynchronous nature of the action. From a software engineering perspective, it is bad because it spreads your code for doing the action.

ES2017 Async Functions

Declaring a function as async lets you await inside for the fetch. Like,

 let result = await fetch(url).then(response => response.json())



 Your code now looks synchronous, and you forget about its asynchronous nature. Moreover, all the asynchronous interaction is now concentrated in one place.

# Structuring the Server Interaction

We usually prefer splitting the server interaction into a server access layer which encapsulates the fetch. That low-level layer is not part of the Redux conceptual architecture for state management.

The Redux action uses the server access layer using await.

The Server Access Layer

Defining a server access layer as:

export const fetchReservation = async (params) => {

  const req = fetch(url)

  .then((response) => {

        console.log(response);

        return response.ok ? response.json() : null;

    })

    .then((responseJson) => responseJson)

    .catch((error) => {

        console.error(error);

        return null;

    });

  return req;

};

# The Action

Define the action as:

    export const loadReservation = async (params) => {

          let reservation = await fetchReservation(params);

          let listing = await fetchListing(params);

          // combine reservation with listing



          if (reservation && listing){

                return {

                  type: LOAD_RESERVATION,

                  payload: {

                        reservation: { ...reservation, listing: listing }

                  }

                };

            }

            else {

                    return {

                      type: ERROR

                    };

            }

    };



    # Iteration with Async Functions



    The fetching of data often has the two-step structure:



    1. Fetch an array of data from one endpoint

    2. Enrich each element of the data by a separate call to a second endpoint

So you have one async function fetching the array, and then you need to iterate over the array, performing another async function.

Using Promise.all, one can wait to the completion of the iteration,

export const fetchConversations = async (userId) => {



    let inbox = await fetchBareInbox(skip, limit, userId);

    if (inbox){

        // enrich the results



        results = await Promise.all(_.map(inbox, async (conversation) => {



            let user = await fetchUser(message.senderId);

            return { ...conversation, user: user };

        }));



        return results;

    }

    else {

        return null;

    }

}

This keeps your code clean and frees you from thinking about the asynchronous nature of things.