React Data Layer Series – Part 1
This post is the first part of an 8-part series going in-depth into how to build a robust real-world frontend app data layer. This first post sets the stage for where we’ll be going in the series. The series will start May 20th and one post will be released daily! If you’d like to be notified via email when the series begins, sign up for email updates.
Even though most frontend apps are backed by a web service, building out the data later for such frontend apps is hard. State management libraries are often unopinionated about organizing your data, so you need to decide that for yourself. The state management libraries aren’t always designed with an eye toward accessing data from web services, so setting up that access can take some work. And although browsers now have good support for running apps offline, actually building a real system to do so is fraught with inherent complexity.
Some GraphQL clients such as Apollo have built-in support for both remote and local data, but you still need to make decisions about when and how remote and locally-cached data should interact. If you want to fully escape that complexity, there are a few off-the-shelf data libraries that handle much of this complexity for you—but their features, pricing, and data privacy won’t be a fit for every project.
With all these challenges, how can we efficiently set up robust data layers for our frontend apps? This blog post series is an attempt to answer this question by demonstrating common patterns for building a robust data layer in the context of a React app. The code we’ll build together can be used as the basis for a React/Redux app connecting to a JSON-based web service. The same patterns can be applied in other contexts as well, such as if you’re building an app with a GraphQL client, another frontend framework like Vue, or a native platform. And if you’re considering an off-the-shelf system like Firebase or Realm, these principles will help you evaluate the features they offer and think through any bugs that come up while integrating them.
We’ll apply these principles over the course of building out a project for tracking a list of video games. On the surface, the features couldn’t be simpler: we’ll display a list of video game titles and provide the ability to add additional games. We won’t even be building the ability to edit or delete games! But the apparent simplicity will highlight the depth of complexity under the surface, as we tackle questions like:
- How will we organize our data stores?
- How will we authenticate to the server? How will we store the access token securely?
- How can we store our data offline in the browser? How can we still provide users access to the latest data while online?
- Should we allow users to make changes to data while offline? If so, how can we handle this?
- Array Rest and Spread, and Object Rest/Spread
- Arrow functions
- Class fields
- Object property value shorthands
This series also assumes you have a basic familiarity with React, Redux, and connecting to web services (we’ll be using the Axios client library to do so). If not, spend some time with the following guides:
We’ll also be using the following libraries and formats, but it’s okay if you aren’t familiar with them. You’ll be able to pick up enough about how they work from how we use them in this guide, and you can dive into them more in-depth later as you have need.
- The JSON:API format for data interchange.
- React Materialize for UI components with a nice look and feel.
- Redux Thunk for deferred actions.
- Redux Persist to save data offline.
Although I prefer and recommend Firefox for general web browsing, in this guide we’ll be using Google Chrome for some of the features its web developer tools provide when it comes to easily working with service workers for offline purposes.
You’ll also notice that we use the Yarn package manager in place of
npm. Yarn connects to the same NPM repository as the
npm client; it just provides simpler commands, better performance, and a more predictable use of lock files. We recommend using Yarn for all professional frontend projects.
React has a lot of different options for state management layers, and Redux isn’t the best choice for everything. Let’s talk through some of the options out there and why you might choose them.
- setState() and the useState() hook are built-in to React. We’ll be using these for transient data. One downside is that it’s local to the component, and passing it around the app can get cumbersome.
- React Context is an API that was made public in React 16.3 and allows passing data through multiple levels of component.
To learn more about these and many other options, check out a blog post about React State Museum, a project to compare different React state management options.
So why are we going with Redux in this case? A few reasons:
- It’s still the most popular state management library in the React ecosystem, so when you do need more than what React provides out of the box, it’s a good choice.
- We’re taking advantage of Redux’s centralized data storage to easily persist our data.
- Redux’s architecture decouples actions from the changes made to individual items of state (via reducers). As we add more richness to our app like different approaches to offline handling, we will change how reducers work with relatively few changes to the actions dispatched. This is what Redux maintainers mean when they say that if you are only using Redux to make data available globally, you probably didn’t need Redux in the first place. Redux is best when you have benefits to gain from the action/reducer decoupling.
- Personally, I have no problem with MobX-style “magic” happening to take care of details under the hood. But one of the advantages of Redux’s explicitness is it can be easier to debug.
That’s all the introduction we need. Check back on May 20 and we’ll get started creating our app!