Data-Fetcher, Provider, Renderer pattern

Josh Guffey
4 min readJun 12, 2021
Three Layers Photo by Siim Lukka on Unsplash

I mentioned this pattern during a meeting this week and some folks I work with thought it was interesting. I had just assumed this was the “right” way that I should think about data in our large-scale React application, but since it surprised some folks I thought I’d expand what I meant in a blog post.

I’m also not sure where the inspiration for this pattern came from. Perhaps it’s extrapolated from the common Presentational/Container Component pattern, but it is a bit different. Either way, I don’t claim credit for this idea.

First some background, I work on a pretty large web application. It’s not as big as something like Facebook, but it’s still got hundreds of components, about 8 Redux stores, and hundreds more data connections to different REST and GraphQL APIs.

We store as little data as we can in Redux. We use our stores only for application-global data, or critical cached data. We also have to be very flexible in a few of our views. For example, we have an entity detail view that requires two api requests, since we only have the ID of an entity but don’t know what type of entity it is and need to know the type before we can query the full data set for the entity. So we cache that sort of data in the global store, as it can save two requests if a user returns to the view later.

Since we have all this complicated fetching logic, I have been thinking of a three level tree pattern for this data.

Level 1: Data Fetcher

Redux requires a bit of boilerplate. When we render a details page if we don’t already have that entity in the global store, we show a loading page and dispatch a load action. We use Redux Thunk to control a side effect wherein we load in the entity details, and then issue a “Loaded” action upon successful fetch. If the fetch fails for any reason we emit a failure action. Pretty standard stuff.

At this details landing page component (the outermost node in the React Tree) we encapsulate all of the redux connection logic. There’s not much to the render function of this component, as it’s basically a switch statement that controls three states, loading, error, and loaded.

Here’s a quick sketch of an example:

Example Code: https://gist.github.com/jguffey/a77b82b108caf30f51599e0f9c96cc39

Level 2: Data Provider

The Data Provider is the component that’s loaded upon success, so we know that if we’re rendering, we’ve got data we can work with! At this level, the primary goal is to push all that selected redux data into the context we’ll need for all of the children. So, this render method is simply a context provider that renders the “Screen” view (which itself is a layout view of all of the details UI).

Example Code: https://gist.github.com/jguffey/e0ceed35c02f9dfb1d17fcafd88a6455

Level 3: The actual components

Since we’ve gone through the above tasks, we can assume that everything has gone well and we now have data we can render in the React Context.

This can make our layout view, and subsequent UI components very easy to code. There’s nothing else we need to load in, there aren’t many async actions we’d even need to account for, aside from a minimal set of actions the user can take on the page.

Example Code: https://gist.github.com/jguffey/23414a295633915a95ce8d701b47c15d

Conclusion

I don’t think there’s anything groundbreaking or new in the patten I’ve described. I just through I’d spell it out for anyone who’s maintaining a large react tree with a lot of network requests to manage. Some of the issues we were seeing before we adopted this pattern were:

  • All the UI components responsible for displaying parts of the details page had to be connected to a redux store, even during automated testing.
  • The UI Components couldn’t safely assume that the data they were trying to render was already loaded, they had to check to make sure they weren’t still in a loading state, or error state. As you might imagine this lead to a lot of bugs and repeated code.
  • Testing and Storybook components was more tedious, as you’d have to remember to prepare a redux store prior to using any of the components in the details view.

There are, however some cons:

  • Split loading: where you’ve only loaded part of the entity data for the page this pattern can get really complex unless you take extra care to segment parts of your data, and even if you do that your UI components might not be able to assume that all the data is loaded by the time they render.
  • There’s a lot more abstraction, the rendering components are disconnected from the logic that loads in the data they display, this can lead to some confusion for developers not already familiar with this approach in our codebase.

Regardless, if done carefully this pattern should help in some situations.

--

--