Sunday, January 31, 2016

Rails, React and ES6

I'm currently working on a side project. I wanted this to be something I can make solid progress on but also learn things so I chose a mix of familiar and unfamiliar tech. The familiar is Rails for the backend and the new is React for the frontend.

However, getting Rails and React to play nicely isn't that easy. Especially when you drop in ES6, babel and browserify. I'm going to come back to all of this, but right now I'm going to talk about one particular issue that drove me nuts and for which Google and Stack Overflow were largely silent.

TL;DR if you get the following error when using a combination of React, Rails and ES6, then read on.

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).

followed by:

Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

The quick answer to why this happens is the following:

When defining classes with ES6 you need to export them as default. See this link for some details about why. If you don't export as default you'll see the error above. So class def should look something like:

class User extends React.Component {
  ...
}
export default User;

Even if you do export as default it means the module is exported with a default property which then messes things up. If you use a require instead of an import you will see the same error again (full disclosure: I don't really understand why yet, JS is new and I need to find out more about what export default does).  To resolve this you can do the following:

window.User = require('./components/User.jsx').default;

This all came to light due to this github issue. As indicated in the link above, this is also an issue in Jest. In fact I think it's an issue anywhere you use require instead of import.

This is a pain because for reasons you need to use vanilla JS in your Rails component.js. That is it for now. I'm going to come back and talk more about the setup to get this stack working nicely, but right now I wanted to get this down in case anyone else ran into it.