You can use CoffeeScript classes with React - pros and cons

… and check why 5600+ Rails engineers read also this

You can use CoffeeScript classes with React - pros and cons

One of the big advantages of React components is that they are easy to write. You create an object literal and provide functions as fields of your object. They you pass this object to a React.createClass function.

In the past React.createClass was a smart piece of code. It was responsible for creating a component’s constructor and instantiating all fields necessary to make your plain object renderable using React.renderComponent. It was not an idiomatic JavaScript at all. Not to mention it broke the basic SRP principles.

It changed with a 0.12 version of React. React developers took a lot of effort to improve this situation. A new terminology was introduced. React.createClass now does a lot less.

One of the most important change for me is that now you can use CoffeeScript classes to create React components. Apart from the nicer syntax, it makes your code more idiomatic. It emphasizes the fact that your components are not a ‘magic’ React thing, but just CoffeeScript objects. I want to show you how you can use the new syntax - and what are pros and cons of this new approach.

A bit of theory - new terminology explained

Starting from React 0.12 the new terminology is introduced. There are now elements - they are an intermediary step between component classes and components. Since before 0.12 children type was not formally specified, we have a new term for that - it is a node now.

There is also a fragment concept introduced, but it is beyond the scope of this blogpost - you can read more about it here.

As I said before, previously React.createClass made a lot of things. It made your object renderable by adding private fields to an object passed. It made a constructor to allow passing props and children to create a component.

Now all this functionality is gone. React.createClass now just adds some utility functions to your class, autobinds your functions and checks invariants - like whether you defined a render function or not.

That means your component classes are not renderable as they are. Now you must turn them into ‘renderable’ form by creating an element. Previously you passed props and children to a component class itself and it created an element behind the scenes. This constructor created by React.createClass now needs to be called by you explicitly. You can do it calling React.createElement function.


{div, h1} = React.DOM

GreetBox = React.createClass
  displayName: 'GreetBox'

  render: ->
    div null,
      h1(key: 'header', @props.children)
      children

React.render(GreetBox(name: "World", "Lorem ipsum"), realNode) # Error!
element = React.createElement(GreetBox, name: "World", "Lorem ipsum")
React.render(element, realNode) 

React elements can be passed to render a component. Component classes can’t be rendered. You create elements from your component classes.

This is a signature of the React.createElement function:


React.createElement(type, props, children)

Where type can be a string for basic HTML tags ("div", "span") or a component (like in the example above). props is a plain object, and children is a node.

A node can be:

  • an element (div(...))
  • array of *node*s ([div(...), 42, "foo!"])
  • a number (42)
  • a text ("foo!")

Node is just a new fancy name for arguments for children you know from previous versions of React.

This is a bit verbose way to create elements from your component classes. It also prevents you from an easy upgrade to 0.13 if you are not using JSX (we got this process covered in our book). Fortunately, with a little trick you can use your old Component(props, children) style of creating elements.

React provides us React.createFactory function which returns a factory for creating elements from a given component class. It basically allows you to use the ‘old’ syntax of passing props and children to your component classes:


Component = React.createClass
  displayName: 'Component'

  render: ->
    React.DOM.div("Hello #{@props.name}!")

component = React.createFactory(Component)
React.render(component(name: "World"), realNode)

Notice that you can still use React.DOM like you’ve used before in React. It is because all React.DOM component classes are wrapped within a factory. Now it makes sense, isn’t it?

Also, JSX does the all hard work for you. It creates elements under the hood so you don’t need to bother.

<MyComponent /> # equivalent to React.createElement(MyComponent)

There is a trend in the React development team to put backwards compatibility into the JSX layer.

All those changes made possible to have your component classes defined using a simple CoffeeScript class. Moving the responsibility of “renderable” parts to createElement function allowed React devs to make it happen.

React component class syntax

If you want to use class syntax for your React component classes in ES6, it is simple.

Your old component:


ExampleComponent = React.createClass
  getInitialState: ->
    test: 123

  getDefaultProps: ->
    bar: 'baz'

  render: ->
    render body 

Becomes:

class ExampleComponent extends React.Component
  constructor: (props) ->
    super props
    @state =
      test: 123

  @defaultProps: ->
    bar: 'baz'

  render: ->
    render body

Notice that getInitialState and getDefaultProps functions are gone. Now you set initial state directly in a constructor and pass default props as the class method of the component class. There are more subtle differences like that in class approach:

  • getDOMNode is no more - if you’re using getDOMNode in your component’s code it’s no longer available with component classes. You need to use new React.findDOMNode function. getDOMNode is deprecated, so you shouldn’t use it regardless of using the class syntax or not.
  • There is no way to pass mixins to component classes - this is a huge drawback. Since there is no idiomatic way to work with mixins in classes (both ES6 and CoffeeScript ones), React developers decided to not support mixins at all. There are interesting alternatives to mixins in ECMAScript 7 - like decorators, but they are not used so far.
  • it handles propTypes and getDefaultProps differently - propTypes and getDefaultProps are passed as a class methods of your component class (as in the example above).
  • component functions are not auto-binded - in createClass React performs auto-binding for all component’s functions. Since now we’re working with a plain CoffeeScript, you got a full control over this binding. You can use fat arrows (=>) to auto-bind to this.

As you can see, this approach is more ‘CoffeeScript’-y than React.createClass. First of all, there is an explicit constructor you write by yourself. This is a real plain CoffeeScript class. You can bind your methods by yourself. Syntax aligns well with a style of typical CoffeeScript codebase.

Notice that you are not constructing these objects by yourself - you always pass a component class to createElement function and React.render creates component objects from elements.

Pros:

  • It’s a plain CoffeeScript class - it is a clear indication that your components are not ‘special’ in any means.
  • It uses common idioms of CoffeeScript.
  • You got more control - you control binding of your methods and you are not relying on auto-biding React performs with the createClass approach.
  • Interesting idioms are getting created - CoffeeScript in React is not as common as we’d like, but ECMAScript 6 enthusiasts are creating new interesting idioms. For example things like higher-order components.

Cons:

  • Some features are not available now - React developers priority with 0.13 version was to allow common language idioms be used in creating React component classes. They dropped mixins support since they can’t see a suitable idiomatic solution. You can expect they will be reintroduced somehow in later versions of React.
  • Developer needs to know more about JS/Coffee - since React does not auto-bind methods in a class approach, you need to be more careful with it. A good understanding of how JavaScript/CoffeeScript works can be necessary to avoid bugs in your components.
  • No getDOMNode can be a surprise - I believe it’ll be an exception, but you need to be careful using available API. Now in React.createClass you can use getDOMNode, but not in a component class. I believe APIs will get aligned in next versions of React.

Summary:

Pure classes approach brings React closer to the world of idiomatic Coffee and JavaScript. It is an indication that React developers does not want to do ‘magic’ with React component classes. I’m a big fan of this approach - I favor this kind of explicitness in my tools. The best part is that you can try it out without changing your current code - and see whether you like it or not. It opens a way for new idioms being introduced - idioms that can benefit your React codebase.

“Rails meets React.js” gets an update!

We are going to release a “Rails meets React.js” update with all code in the book updated to React 0.13 this Friday. All people who bought the book already will get this update (and all further updates) for free. It is aimed for Rails developers wanting to learn React.js by example.

For the price of $49 you get:

  • 150~ pages of hands-on examples, basic theorethical background, tips for testing and best practices;
  • 50~ pages of bonus content - examples of React in action, more advanced topics and interesting worldviews about creating rich frontends;
  • a FREE repository of code examples bundled with the book, so you can take examples from the book and fiddle with them;

Interested? Grab a free chapter or watch a quick, 3-minute overview of it now. You can buy the book here. Use V13UPDATE code to get a 25% discount!

Join the group of 350+ happy customers who learned how to build dynamic user interfaces with React and Rails!

You might also like