Why we moved to React

Back in 2012, when Instacart was just getting started, the site was created as a large, single page app written with Backbone, jQuery, Underscore and Haml. This fit well with the underlying Rails stack and, given that most of our engineers criss-crossed between the backend and frontend lines, it made sense to have our frontend codebase style match our backend’s. Considering our speed of growth and output, this worked exceptionally well.

Fast forward a couple years… our needs and outlook had changed dramatically. Our team had grown many times over, our customer base had become much larger and the number of retail partners we work with had gone through the roof. The complexities of the app now had to be managed by a larger team across a larger number of ever-increasing features and special cases.

Our Backbone app was starting to get cumbersome, slow and hard to debug. And it was only going to get worse. We needed to make a change… but why React?

Mostly Compatible with Backbone

As mentioned earlier, we had a huge Backbone app already running the majority of the site. To completely rewrite the site in one attempt would be incredibly difficult, bordering on insanity.

Luckily, React doesn’t require a total buy-in to get started. In your traditional Model/View/Controller pattern, React can act as just the View layer. This allowed us to maintain our Backbone models while converting the Backbone Views into React Components.

However, this did create a few complications. For one, React components using Backbone models for props or state need to know when those models update. Easy enough, just use Backbone’s built in event system to listen for changes. (We write in Coffeescript right now).

Make sure your React component stops listening when it unmounts. If you don’t, you’ll run into memory leaks as your React code waits around for model changes to update a no longer mounted component. If you’re calling setState() in response to these triggered events, you’ll get a nice warning from React.

The other big issue we ran into was the urge to use Backbone models as props and then call set() on the model in response to other activities. If you’re used to writing Backbone apps, this is a hard pattern to stop. Props in React should be treated as immutable. If you need to change a prop, move it up to the parent component as state, respond to changes there and then pass it down to your component as a prop. Facebook has a great post about determining where your state should live.

State Management

State management in Backbone is easy enough when your app has only a few interacting views. Usually, you either create a model or just a regular object in memory and go from there. This pattern breaks down quickly without some kind of architecture to manage the state between an increasing number of Views/Controllers. And, when rendering is expensive (thanks DOM), you write more code trying to guess if you should render at all. Unfortunately, Backbone doesn’t have a great solution to this problem and most groups end up creating an in-house state manager that has knowledge of everything.

In React, your components should be built only to care about their own state and props. State is moved up as high in the hierarchy as possible, so that fewer parts need to know how or why a state changes. The idea of props, which is mostly immutable data passed down to a component by its parent, really helps keep your components small and self-contained. This style of component removes much of the complexity around how state is managed and passed around your app. You can never escape complexity, but at least now you can control it in a standard and predictable way.

Rendering Speed

Rendering in Backbone is generally done by pushing your data into some kind of template and appending it to the DOM with jQuery. This is fine for smaller apps, but when there are dozens of views interacting with each other and rendering at once, things can get nasty. Manipulating the DOM is expensive, and our Backbone app was doing it frequently. Adding items to cart, chatting, showing notifications, updating your in-route order and so many other things can all be happening at once on the site.

A lot of folks first hear about React because of its rendering speed. It keeps an internal model of the DOM in memory, and uses it to compute which parts of the actual DOM need to be changed. This removes the need for checking if a component should render and makes that rendering even faster. React also queues up state changes and takes action on the DOM all at once, to further reduce expensive rendering. When we converted a Backbone view to React we noticed a much faster rendering speed with less code.

If even this isn’t fast enough, you can use shouldComponentUpdate to determine if you want to stop React from running the calculation on its internal DOM. More on that from Facebook here.

Reusability

There are a number of small pieces of the site that are almost the same, but different for important reasons. For example, grocery items appear all over the site in all kinds of ways. The data model for these items is always the same, but the views using that data have many kinds of different functionality for favoriting, zooming, adding/removing to cart, quantities, pricing conditions and so much else. It’s hard to make a single ‘item’ view in Backbone without making tons of smaller sub views and huge lengths of state management code. It’s also expensive to have that many views manipulating the DOM.

With React, you can create as many components as you need and conditionally include them in your component hierarchy as you need them. This is great for us, because we have a lot of shared functionality across the site, but in very small packages. Because our models are consistent, we can mix and match all these small components together to quickly create complex, but manageable components.

Joy

Lastly, but possibly most importantly, creating things in React can give you a feeling of javascript zen. Most people recoil at the look of JSX code the first time they see it. Granted, learning to make things in React does require unlearning a great deal of past rules. However, once you’ve created a few complex projects, you’ll never go back. Rendering without worry, logic all in one place and an amazing community of smart developers to lean on really makes the act of writing React components seamless.

Looking Forward

While most of the Instacart site’s Backbone views are now converted to React, we still have a bit to do. We’ll most likely switch over our Backbone models to something more compatible with reactive style data flows. As an experiment, we tried writing a separate app to try out the popular Flux library. When you rate an order on the site, you get a special single page app that uses React and Flux. No jQuery, no Backbone.

We have a ton of work to do on the site, but it’s nice to do that work in a clean and enjoyable way with React.