«

Top 5 React "Gotchas"

React is an open source library for "building User-Interfaces", open sourced by Facebook. If you're familiar with the traditional MVC pattern, React would be the "V" layer of that paradigm. I would even go so far to say that if you're not careful, React can also become the "C" layer as well. I've built a pretty slick deal site with it, and I can say with fair certainty that I think that React is the way forward and all other frameworks will follow a similar pattern soon.

Either way, React has been getting a TON of press recently, a lot has to do with React Native's pending release, but given the amount of press and hype there is still very little information when compared to other libraries and frameworks (Angular, Ember and BackBone all have a good slice of Stack Overflow posts). Given that, I thought it'd be a good idea to compile the top 5 "gotchas" when working with React:

#1 Props vs State

One of the first things you encounter when breaking ground with React is this.props and this.state. They do very similar things, and are available for referencing just about anywhere during the component life-cycle, but are really designated for certain behaviours.

Props are designated for handing your component outside data. If you think about standard library getters and setters, props are what you'd pass into a setter. They come from outside of the component, and the component shouldn't generate it's own props. Another "gotcha" is that you also hand callbacks to components via props. For instance, if someone clicks a button, and it needs to trigger an outside action, you'd pass a callback into the component via a prop. This is the preferred way for handling data changes as your app is being used.

Here's an example of this.props passing in a callback:

var button = React.createClass({  
  render: function() {
    return (
      <button onClick={ this.props.onClick }>{ this.props.buttonText }</button>
    );
  }
});

State, on the other hand, is private to the component. Ideally, you should steer away from state in React to keep them reusable. That being said, state is useful for managing a components internal, well, state. For instance, if you want your button to change it's css class after being clicked:

var button = React.createClass({  
  // Setup initial state on mount
  getInitialState: function() {
     return {
       clicked: false
     }
  },
  onClick: function() {
    this.setState({
      clicked: true
    });
  },
  render: function() {
    var buttonClass = this.state.clicked ? 'clicked': '';
    return (
      <button className={ buttonClass } onClick={ this.onClick }>
        Click Me!
      </button>
    );
  }
});

#2 You'll need a build system

React defines a new subset of JavaScript called JSX. This XML-JavaScript mutant syntax is something browsers won't understand if you were to simply drop your React application in a page. To do that, you'd have write code like this:

React.render(  
  React.createElement('h1', null, 'Hello, world!'), // BOO!
  document.getElementById('example')
);

Or, implement a build-process so you can write code like this:

React.render(  
  <h1>Hello, world!</h1>, // Better!
  document.getElementById('example')
);

While this might not be that obnoxious at first-glance, writing deeply-nested views will very soon become combersome. I should also note that there is a client-side transformer you can use, but if you're at all serious about your app's user experience, you'll compile your JSX before deploying.

I know a lot of people will probably kick and scream about compiling their JavaScript, but you'll gain so much from having done so with access to ES6 compilers and even easier acces to things like unit-testing and code-coverage. It's an investment that will pay out in the long run.

#3 You'll want to use Babel (formerly es6to5)

The React JSX transformer comes with a some options to compile your JavaScript with some harmony options. This, essentially, means that you can have nice ES6 things like destructuring, Arrow functions, Spread Operators and a TON more. However, the React transformer can't do what Babel can, and Babel does it quicker in my experience).

Babel is a JavaScript compiler, that whilst being able to compile ES6 (and some ES7) also compiles JSX! There are already ways to plug Babel into your existing build (webpack or browserify), and since you are going to have a build system, it's easy enough to drop into your project.

Now, maybe you don't want to use ES6 just yet. But there is a good chance you'll want to use some other components. Maybe even the slick Magic Move you've heard about. Well, the bad news is that a lot of these components use ES6 in their source (and some don't compile them down to standard JavaScript). Therefore, at some point you'll want to have this ability just so you can take part in the awesomeness that is open source React components.

#4 You'll want to have a good UI heirarchy

One of the downsides of React is that you have to write some more code than your used to get things setup. Every time you'll write a component, you're going to write a render method and likely a componentDidMount and some event handlers.

On top of that, React is structured to flow data down through components. You'll generally have a top-level page component that will get data/state from somewhere, and pass it into the "child" components. Once this structure is in place, it's hard to make alterations later that won't cost time.

Let's take, hypothetically, a blog with a Search component and list of posts. In a previous framework, you might have a page that does something like:

Blogs Page  
|--> Search View
|     |
|     V
|    (Search text goes directly to posts to filter)
|     |
|     V
|--> Posts (Filter themselves based on text matches)

In this type of setup, search entries are passed, via events or some DOM manipulation, into posts so that they can hide themselves if the text isn't relevant. This might not sound problematic in the onset, but React doesn't really offer a way to have child-to-child events. You can implement it, but I'd recommend doing something like:

Blogs Page  
|<--- Search View (fires search events up to the parent component)
|
V Blogs page removes irrelevant posts  
|
|--> Posts (ignornant of searched values).

This type of data-flow is the first half of what Flux set's out to do, and pairs well with React. Because of this, I tend to think of the page-level controller as a ViewController since it handles business logic and managing page-state.

#5 Keep your views "skinny"

It's easy to have your views handle all your applications business logic inside the React component. Since you can handle events and call parent components via callbacks, a naive approach would be to stuff your views with all sorts of logic. However, you'll begin to walk a dark path into testing-hell as you won't be able to test individual units of code.

What do I mean? Well, Reacts testing harness Jest operates by mocking DOM via jsdom and runs them on the command line. What does that mean?! Well, it means every time you run your tests, you're going to have some setup overhead. Which, if you've ever had more than a handful of tests to run, means your tests will quickly become slow.

It's much easier, and faster, if you:

  1. Construct your logic in a "pure" function manner (functions that only manipulate arguments passed into them, and return a value).
  2. Put this logic in a separate file for testing.
  3. Test all your edge-cases via returned values from these functions.

This way your tests are lightening fast (no need to mock DOM or setup a bunch of headless browsers), and it's much easier to test units of code and their edge-cases.

Summing It Up

React is the first library where I feel they "got it right" in regards to webapps. It just represents the view layer of an application, and if you structure your app properly, should only represnt your application data and not manipulate it. When paired up with a flux-type architechture, you'll have a much easier time reasoning about how state is managed in your app and enjoy working on it more.

Share Comment on Twitter