Using JSPM, ES6 React, and React-Router

I recently started the process of porting an Angular 1.x web app I had to React (ES6, package managed with JSPM), and am having a pretty good time writing the code for the app. This was less of a port and more of a complete re-write (as none of the code from the angular version is really re-usable, per say).

If you’re not familiar with ES6/ES2015 features, I highly recommend Mozilla’s blog series on ES6 Features, written (in most part) by Jason Orendorff. They’re an excellent resource on the new additions (and way more — stuff that didn’t make it in, stuff that is rarely covered) made in ES6.

If you’re not familiar with JSPM, I’d suggest that you visit jspm.io and/or watch some of the videos that are hosted on their page (linked below):

(as you can see, that is actually an introduction to using JSPM and ES6-powered React together)

One of the things that React doesn’t come with (and shouldn’t come with, as React is a library, not a framework) is a capable SPA router (if you’re into that sort of thing), however that’s certainly fixed with the advent of React-Router. React-Router offers a fantastic blending of modern SPA routing techniques (inspired by Ember’s router) and the declarative API that powers React. I’ve found using it to yield very little surprises and have intuitive behavior, which (although incredibly subjective) is always a huge plus to me.

One of the problems that I recently ran into is this error:

Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's render method). Try rendering this component inside of a new top-level component which will hold the ref.invariant @ invariant.js:42ReactOwner.addComponentAsRefTo @ ReactOwner.js:70attachRef @ ....

This error description is pretty opaque (and I didn’t get any specific file or line number to look at), but after thinking about the problem and googling for a while, I found that the solution (most of the time) is to make sure that you don’t have two instances (differing versions) of React loaded at the same time.

In my case, this meant running a simple command from my project root (to find folders that might indicate react installs):

find . -name "*react*" -type d

The output I got looked like this:

./jspm_packages/npm/react@0.14.0-beta3
./jspm_packages/npm/react-dom@0.14.0-beta3
./jspm_packages/npm/react-router@0.13.3
./jspm_packages/npm/react@0.13.3

It looked to me like react-router was depending on an older version of React (0.13.3), and this is what was causing two versions of react to be loaded (and thus the error to occur). While there were some good reasons to use React version 0.14.x , I went ahead and used JSPM to resolve the dependency issue by downgrading to React version 0.13.3.

Luckily, JSPM has a command just for resolving dependencies (which should be used with some care):

jspm resolve --only npm:react@0.13.3

The output looked like this:

Primary install 0.14.0-beta3 -> 0.13.3
     In npm:react-dom@0.14.0-beta3 0.14.0-beta3 -> 0.13.3
ok   Resolution to only use npm:react@0.13.3 completed successfully.

This then required me to change the code that was using features introduced by the 0.14.x version, to utilize the older React API (here’s the diff):

--- a/app/main.js
+++ b/app/main.js
@@ -1,6 +1,5 @@
 import 'fetch';
 import React from 'react';
-import ReactDOM from 'react-dom';
 import App from './components/app';
 import Landing from './components/landing';
 import Router from 'react-router';
@@ -13,8 +12,6 @@ var routes = (
     </Route>
 );
 
-
-
 Router.run(routes, Router.HashLocation, (Root) => {
-  ReactDOM.render(<Root/>, document.getElementById('app'));
+  React.render(<Root/>, document.getElementById('app'));
 });

Outside of this error periodically showing up, the development experience has been pretty pleasant with these tools. It’s definitely made me feel very productive (if not actually making me more productive). The only real worry I’ve had is whether I was creating “too many” React components, and trying to ensure I was using ES6 react properly (ex. getInitialState() is implemented by using ES6 constructor function instead of a standalone function).