Rendering Backend Requests with React

Emil Ong
Haus Engineering Blog
6 min readMay 26, 2016
Arrows in flight, in the quill, and dropped on the ground (archers, by Steve Slater is licensed under CC BY-2.0)

One common pattern that I’ve come across in building a React app is having a page or part of a page that depends on some data from a backend service. In other words, you may have a component that you cannot render without data from the backend and that component is the main reason why the user is looking at the page or a part of it. Thus you might say that that part of the view is synchronously dependent on data from the backend. This post gives one pattern for dealing with such situations including dealing with loading and errors.

I’m writing this post based on my experience with React and Redux, but there may be value for folks not using Redux and I’ll try to extract some general patterns.

You need two views for backend data

As I discussed in a previous post, there are actually two important aspects of a call to a backend API that your frontend application needs to care about:

  1. the data fetched and
  2. the metadata about the call itself

The former is pretty self-explanatory, but metadata is often overlooked. What we need to realize is that from a UX perspective, our user actually cares about the state of the call, not just its result and thus we need a view for it.

The user needs a view for the request, not just its successful result.

Thus for these two types of data, we need two views. The interesting thing about these views is that they occur in the same space on the screen. The view for the metadata will usually be one of:

  1. Invisible — the data has been loaded and is displayed by the data view so the request is irrelevant.
  2. Loading (e.g. with a spinner)—there is a request in flight and the user needs a hint that something is happening.
  3. Error—the request failed for some reason, either a user issue, a server issue, a connection issue, et al.

A React Pattern

We can pattern these two views in React with components for each of them. Because they will occupy the same screen space however, we can compose them together in some way. Let’s call the component that deals with the request the “metadata container component.” It effectively mediates whether the data component is rendered and what data it receives.

We can implement this either via a higher-order component (HOC) or simply a container component that renders the data component conditionally as its child. I would recommend against creating mixins. See the HOC article for details on that.

Given this structure, let’s look at the scenarios that we could encounter with our components. Injected into our metadata container are the two types of data. Given that, we can completely describe the behavior of the components in a nearly functional way, except for the asynchronous backend request itself.

This diagram shows us with the initial mounting of the components having not requested or received any data. Given that scenario, we simply fire off an asynchronous request. We need to annotate that the request happened however, which we do by firing an action into our store (Redux, for example) which then triggers a refresh.

In the second scenario, we’ve initiated a request and the store injects that request, but no data as of yet. In neither of these cases are we rendering the data view at all.

When the request completes successfully, we push the data and the request metadata back into the store which is delivered to the container view. The view should be able to determine that the data has arrived and can use a selector to extract the data, then pass it to the data view.

When the request fails, the container view receives the metadata about its request, but no data of course. The container then prevents the data view from rendering, but does render its view of the request failure itself, e.g. a sad face or fail whale. 🐋

Bonus round! One of the great advantages of using a store like Redux for keeping your backend data is that you can reuse that data in multiple views. If you have the data in your store that was initiated in one place, your metadata container view can simply recognize that and pass through the data without initiating a new request! (This scenario is probably the same as the one above in which the container initiated the request, as far as the React component is concerned.)

What could possibly go wrong?

Hopefully this all seems logical and makes sense, but when we actually implement containers in this way, we run into a few questions.

How do you select the right request for the right container?

If you’re using Redux, you can use the mapStateToProps function to select out the right request, but this means that you need some way to identify your requests canonically, e.g. “Select the request for a widget with id 37.” Conversely, that means that your actions need to store your requests in your store canonically. There’s a decision to make here though about history. Have you requested widget 37 recently? If not, should you request it again or is it probably the same?

How does the container tell the difference between a request that returned nothing and data that has not yet been fetched?

If you’re lucky, it should be easy to tell this by looking at your requests. I.e. for a single item with an identifier, you will have a single request and that item is immutable. Unfortunately, that’s not always possible…

Imagine that the data you’re fetching can be received from multiple calls, e.g. an index call or a call for a resource with a specific identifier. Maybe the item can be normalized from another resource that has the data nested within it. You may have your store structured in such a way that selecting data from it for the item you’re seeking would return undefined or an empty array if either the item does not exist (404) or does but you just don’t have it yet.

In these cases, I’m currently checking first for a request for that item, then for the item in the store, and, if neither exists, initiating a new request. I’m not sure if you’d call this optimistic or pessimistic, but it does the job. :)

Other possibilities include “normalizing” your requests reference the request for an item, regardless of whether the request was for it specifically or not.

Related work

This approach is very similar to that of Relay, but slightly abstracted as I’ve described it here. In particular the concept of data-masking is very similar to the metadata container. The difference in mindset here is that Relay has snippets that it renders at pre-specified points in the request cycle whereas I’m suggesting that we’re actually rendering a view of the request itself. Perhaps the delta is simply conceptual, but in practical terms, your metadata view attached to Redux in this way may get more data from the store than the predetermined attributes on the “view callbacks” of Relay.

Conclusion

Figuring out the structure of your React components is basically the essence of working with React, so hopefully this article gave you a useful model in which to think of both your requests and how to filter down data from them into your page.

If you’re interested in working with us, discussing issues like these and others, please check out our jobs page and get in touch!

--

--