Dealing with asynchronous code in React components

The React render function must be pure and always return a DOM element. This poses challenges when the underlying data is fetched asynchronously, and thus may not be available at the time when the component needs to be rendered. I wrote a small library to help me dealing with this problem. It is called Computation.

The React documentation has a short section about how to deal with this problem. They suggest to store the data in a component's local state. Start fetching in componentDidMount and update the component's state when the data is ready. However, often you want to share the fetched data between multiple components. Or fetch it once and then cache it, even when the whole UI gets re-rendered afresh. For these cases you'll usually have some kind of external repository which manages the data, such as Backbone collections.

During rendering, this external repository may or may not hold the data you need, and you must handle the case where the data is not available (for whatever reasons).

A datum in that repository can be thought of as in one of the following three states: pending (eg. the XHR request has not finished yet), present (the datum was successfully loaded and you can use it) or error (the datum was no loaded due to internal or external errors). Whenever your rendering code wants to pull it out of the repository, it needs to check in which state it is and handle appropriately.

This would be straightforward to handle if you just want to use a datum as is. But often you will want to process it in some way. Or combine multiple data into a single output value. For these situations it is desirable to have a set of functions which you can easily combine, and which automatically handle the pending and error cases for you.

The Computation library does just that. It allows you to combine functions such that the uninteresting states are handled automatically. Pending and error cases are propagated through the computation, and you can write the functions as if the data was always available.

In the context of React, the computation must always result in a meaningful value. For that reason, if you want to actually get the result, you have to provide a default which will be used in case the computation is pending or failed. Here is an example from our code:

var gravatarUrl = rmx.data
    .findById(userId)          // Fetch the user record from the server
    .fmap(generateGravatarUrl) // Compute its gravatar url
    .get("http://...");        // Use this default if the user is not loaded yet

return <img src={gravatarUrl} />;

If you are familiar with Haskell, you can think of Computation as MaybeT, except that errors are explicitly carried in the type.

The library is currently being used in a large client-side application. It only provides fmap, bind and liftA2 combinators, but more can be added as needed. The code is fully tested with 100% code coverage (not suprising, as it is mere 104 lines long).