This is a bonus post in the Cabin tutorial series created by getstream.io.

Visit getstream.io/cabin for an overview of all the tutorials, as well as a live demo. The source code can be found on the Stream GitHub repository for Cabin, and all blog posts can be found at their respective links below:

Introduction React Redux Stream Imgix Keen Algolia Mapbox Best Practices and Gotchas

At Stream, we’ve chosen to use React and Redux to power both our own dashboard and our example application, Cabin. This decision wasn’t based (solely) on the fact that React is the new hotness in the Javascript world. Stream stands by this combo because the two are simply good at displaying data and managing state, and we're not the only ones who feel this way. React and Redux power scalable, production-level applications used by millions of people every single day.

Goals for this article

By the end of this article you should be able to:

Clearly articulate best practices when it comes to React and Redux.

Avoid common React/Redux pitfalls.

Structure a production level application that can scale.

Utilize tools such as Immutable.js, Chrome’s React developer tools, and middleware, such as Raven.

Understand the React event system.

This post is not:

Please take a moment to read Thinking in React, as the article helps one see the advantages of pairing React and Redux.

A tutorial – there will be little code displayed in this post. If you’re interested in something more in depth, take a look at Cabin, a series of tutorials that results in a fully functional application.

A deep discussion on MVC frameworks, such as Angular. We could go on for days about which framework you should go with; however, we're not talking about this subject.

With all of that said, let’s get started!

Gotchas

A Case for Using Immutable Data Structures with Your React Application

A quick refresh on what immutable data structures are:

Mutative API’s yield a new instance instead of changing the old object. This model aligns well with the architecture of React and Redux since data is passed from above rather than being subscribed to.

If you have read about the advantages of using immutable data structures inside your React and Redux application, but are not fully convinced you should use them (standard Javascript objects seem to work equally well right?), let me explain why you should always start a new React and Redux project with immutable data structures.

As you have learned while reading the Redux documentation, a reducer is the only place that alters the application’s state. It is a pure function that receives the old state and an action as inputs and returns the next state. If you try to use a reducer to mutate input, Redux will warn you that this is not a reducer’s intended function. Mistakingly mutating the old state here is rare and using an immutable object to represent the state not necessary.

But what happens if we use mutable objects to contain the state tree and mutate these outside of a reducer? Say we want to enrich some part of the state tree while mapping the state to properties of a certain React component (e.g. inside the “react-redux” connect method).

function mapStateToProps(state, ownProps) { let activities = state.feeds[ownProps.feedID]; return { activities: enrichActivities(activities, state) }; } function enrichtActivities(activities, state) { for (let activity of activities) { // Read user from state by user id activity.user = state.users[activity.userID]; } return activities; }

React does not warn us of this “illegal” mutation, neither does Redux. The application does not crash immediately, but we have now altered the application-wide state object. This means the state has changed by a side-effect other than a reducer function.

This scenario can result in major headaches for developers when parts of the application seem to be breaking without any apparent reason. You can see that the state of the application is wrong with the help of Redux Dev Tools, but can not find out why and or where it was mutated.

Using a library like Immutable.js mitigates this issue because it makes it impossible to mutate the state tree in any part of your code. Every operation performed on the state object will yield a copied instance, instead of changing the current one.

Designing the Redux.js State Tree

Designing the state tree is one of the first actions you will take when starting up a React/Redux application. It is possibly a good idea to consider several tradeoffs implicit in various design decisions before you start coding up your app. Here I will discuss two such tradeoffs.

Often, you want to store a list of items retrieved from your backend inside the Redux state tree. At Stream, for instance, we would need to store activities retrieved for a feed. You have two options for storing these.

Either you store them inside an object where each key is the activity id and the value the activity. Or you store them inside a list where each item is an activity.

The first approach has an advantage that it is easy to get activities by id from the state tree, as you only need to select a member by activity id. On the downside, you can not guarantee any order. Storing the activities inside a list allows you to guarantee ordering but selecting one item from the list by activity id means traversing the list.

Denormalized or normalized state?

Once your app starts growing more complex entities in the state tree will start referencing other entities. For example, in todo applications, todo lists may contain multiple todos. Your API might return one todo list nested with multiple todos, and thus it might seem like a good idea to store the todos that way in the state tree:

{ "title": "Todo List", "todos": [ { "text": "Consider using Stream", "completed": true, }, { "text": "Finish reading the Cabin blogpost series", "completed": false } ] }

This design decision makes it much harder for future actions/reducers to alter any of these todo items. They would have to be aware of the parent todo list before they could alter any todo inside it. Also moving a todo to a different list is more complex.

Instead, try to normalize all relationships inside the state tree, either by keeping a list of references from a parent entity (i.e. the todo list) or keeping a “foreign key” in reference to the parent entity from a child (i.e. a todo item). These two approaches have different advantages, in the first approach, it is easier to order children, whereas moving an entity to a different parent is easier with the second approach.

Pro Tip: If your API returns deeply nested items and you need to normalize them on the client, the normalizr library might come in handy.

React's Synthetic Events

Inside React event handlers, the event object is wrapped in a SyntheticEvent object. These objects are pooled, which means that the objects received at an event handler will be reused for other events to increase performance. This also means that accessing the event object’s properties asynchronously will be impossible since the event’s properties have been reset due to reuse.

The following piece of code will log null because the event has been reused inside the SyntheticEvent pool:

handleClick(event) { setTimeout(function() { console.log(event.target.name); }, 1000); }

To avoid this you need to store the event’s property you are interested in inside its own binding: