Upgrading from React 0.11.2 to 0.14.7 in 374 easy steps

Today I set out to the task of upgrading a set of views crafted with the React library. The upgrade was necessary because one part of a view using React stopped working in Chrome Version 48.0.2564.116 (64-bit). I don’t know why this happened, I didn’t find anything related to it and as it stands this was probably the only project on the Internet still running on this old version of React anyway. If not, you may or may not find this post useful. Or not. But at least it might be entertaining.

The post is divided in two major parts:

  • a rough description of my day in all of its beauty
  • a short reflection on this experience

Disclaimer

This is not a plain Javascript project. Had this been a plain Javascript project, things would have been a lot easier. I mean, still maybe not that easy, but you know, somewhat more agreeable. Keep this in mind while you read the first part.

The upgrade path

Element Factories

As of React 0.12 React components need a factories in order to be created. It’s all described here. Upon reading the article I couldn’t help but wonder how long it will take until React will have its own AbstractSingletonProxyFactoryBean but let’s just not get into this right now.

Along with a few deprecation warnings prompting me that a few methods had moved from React to ReactDOM, this mainly meant that code that looked like this:

now had to look like this:

That still wasn’t too hard, simply requiring react-dom, figuring out the correct paths to give to requirejs and off to the next step, which is where the real fun begins.

JSX transpilation

React uses JSX views to… well, to be React. So those need to be “transpiled” (that’s the hipster word for “compile”) to plain Javascript.

(at this point, if you come from Javascript-land, as you continue reading, you will start to slowly but surely raise your eyebrows and hear yourself mumble the words “what the hell” repeatedly)

So when you have an SBT project the way to go is to use a plugin for sbt-web which is the library that lets you set up pipelines for Javascript, CSS etc. And so for React you use the sbt-reactjs plugin which takes care of JSX transpilation.

Except that this project is no longer maintained. So then off you go looking at the forks of that GitHub project, hoping that anyone has taken over the project. While I am at it, it would be very nice if GitHub made it possible to switch over the ownership of a project to another fork instead of continuing to promote the initial (but no longer maintained) version of that project.

Looking at https://github.com/ddispaltro/sbt-reactjs/network it looks like lglossman had a fork that switched over to the babel compiler since the JSTransform tool initially provided by Facebook is, you guessed it, no longer maintained.

So full of hope and renewed energy you set out to use this fork, which means that since it’s not published you need to build it yourself, i.e. cloning the project, configuring build.sbt so that the project is compiled using “maven style” (publishMavenStyle := true) and publish the library locally using sbt publish. The target project having already a custom local repository for this kind of situation all that is left to do is to copy the locally published artifacts from the local ivy repository to that repository.

What happens next is fun:

Can you spot the problem? Yeah, me neither.

Google is misleading on this one, it points to e.g. [this closed issue])(https://github.com/sbt/sbt-less/issues/38) which is about a less module, but has nothing to do with the root cause in this case.

So really the only way to make sense out of this error is to run SBT with the debugger turned on:

and to connect to it with your favourite IDE, and to download the source of the sbt-js-engine which blows up and to put a breakpoint here, to see what it is that we get back from the evaluation of the Javascript script that takes care of JSX transpilation

And that gives you the following insight:

(so there probably is a problem in the way the sbt-reactjs plugin reports errors, or something).

This time Google is your friend. It turns out that the /** @jsx ... */ annotation that was previously required for JSX transpilation to work [https://github.com/skratchdot/react-bootstrap-multiselect/issues/5](crashes babel). Alright then, removing it indeed fixes the issue. Let’s go to the next step.

Stricted DOM nesting validation

At first, a warning:

So I can’t have a paragraph somewhere inside of a paragraph, which makes sense. And that’s good and all, but where does this nesting happen in the 425 lines of this component I didn’t write on my own? It would be nice if the stacktrace would give me a clue as to where to look for this nesting… at this point I have no other choice than to search for p‘s in the component and evaluate its execution in my head.

There were a few more instances of invalid nesting in the project, namely related to tables, all related to invalid HTML, such as

But now at least the DOM is clean.

transferPropsTo is deprecated

As described here, transferPropsTo is no longer. As a result the next error message is:

This is a fairly easy one to fix. Namely, this:

turns into that:

After this step, things seem to work.

And that’s it

Finally, things start to work again, and lo and behold, the project works again in Chrome Version 48.0.2564.116 (64-bit).

Reflection

Everyone who has been in the IT business for a while knows that upgrades are not fun. Today was not really fun, even if I may have made it sound so. Everything keeps on breaking and while at it you don’t really know when the flow of stacktraces will quiet down.

In one way, today’s experience reminded me of Eclipse (the IDE). Jetbrains won the race mainly because they made one consistent product that didn’t break at each upgrade. I switched to that IDE in 2008 and never looked back because back then at least upgrading Eclipse meant spending half a day trying to get all of your plugins to work again.

But perhaps it’s not so much the upgrade which is painful here. In fact, React itself is farily well documented, with changelog and all, and with small articles detailing breaking changes. That’s not the case for most libraries out there. The real painful part of the process is caused by everything around React, including the build tools.

Would things have been easier if the build tool was directly a Javascript tool and not an SBT wrapper around a Javascript tool? Maybe so, but I can’t answer this question because I don’t work on a 100% pure Javascript project at the moment. Though I would suspect similar problems to arrise – after all, the root of the complexity lies in the interaction of all the moving parts.

So I think there are two things at play here: the high speed at which Javascript libraries evolve and a culture that does not favour backwards compatibility nor maintenance (see Generation Javascript) coupled with React’s “library” approach that leads to the “moving parts” problem (see Javascript fatigue).

After all, this was just one not so fun day, and I’m ok with this – it isn’t the first time and certainly won’t be the last. But I’m lucky enough to have the expertise required to get through such an upgrade without loosing my mind and literally flipping the table, although my distinct impression is that the “upgrade pain” gets worse as I get older, despite the fact that I know more year by year. In my previous life as a Java developer, things tended to get easier with time, because I had unconsciously memorized the stacktraces and knew fairly well that some arcane error message meant that I needed to add or remove a cryptic annotation here or there.

These days things seem to have changed. I’ve been more or less in constant touch with Javascript and its ecosystem for a solid 8 years now and yet things don’t seem to get easier, to the contrary. So I don’t know, maybe it’s just me getting more conservative / impatient / you name it or maybe, just maybe, it’s the ecosystem that gets harder to work with, especially in the recent 2-3 years.

I think that for someone who just gets started on the job an upgrade task of this kind of task might feel a lot more frustrating. Perhaps so frustrating that they might flip the table, throw everything away and rewrite it from scratch.

One thing seems increasingly clear to me: this way of building software is not sustainable. What can we do?


Subscribe to updates

Liked this post? Subscribe to the mailing list to get (monthly) updates on what I'm doing here.

12 Comments Upgrading from React 0.11.2 to 0.14.7 in 374 easy steps

  1. Sorin

    The first problem was maybe starting a project with React version 0.11? The project was honest telling people “not stable yet” – which of course doesn’t mean nobody should touch it (how would projects stabilize otherwise) but should be understood as “work in progress, expect havoc”. Now whether this is a general issue of “generation JS” or not, it’s a matter of debate… Either way, I feel your pain 🙂

    Reply
    1. Manuel

      I actually didn’t know that 0.11 wasn’t stable – then again I didn’t write the React project and am only maintaining it, which kind of only adds to the pain, in one way. Thanks for feeling it!

      Reply
  2. alyandon

    Thanks for taking the time to write this. I had a good chuckle at the spray.json stack trace even though it brought back some painful memories. 🙂

    Reply
  3. Kraig Walker

    Sounds like you’ve been dragged through a lot of pain, Manuel. Hopefully from React 15 onward update paths will be clearer, and documentation for upgrading projects will become more official.

    While I’ve kind of ignored a lot of the specifics of what the Angular team are doing, the amount of effort they’ve put into giving Angular 1.x devs a path to upgrading to their next major version has probably solidified its’ reputation as a stable platform for enterprise.

    Sometimes (sometimes) I feel like much of my enthusiasm for React is driven by big names endorsing it, and I’ve given a lot of trust over to open source projects developing fringe projects (react-router, redux etc) Sometimes it’s Reacts most brilliant strength.

    I guess when I’m having a bad day it feels kinda terrifying.

    Reply
    1. Manuel

      The upgrade path for react itself wasn’t that bad, actually. It’s updating the tooling for JSX -> JS compilation that appeared to be most problematic. That’s when all those moving parts required to make a react application run kind of get in the way, because upgrading one means upgrading all of them.

      But maybe that’s just the cost of upgrading, which I suppose, while not being very fun could be overweighted by the advantages of using this technology. What bothers me most in this case is that the upgrade was necessary because the application broke on a new version of Chrome. That sort of backwards incompatibility could become fatal to many little projects that do not have the resources for maintenance or simply don’t think about it (many sites get deployed w/o much maintenance afterwards).

      Reply
  4. Jennifer

    You wrote about “the high speed at which Javascript libraries evolve”. Yet when I hear about incidents like this, and this is just a minor one compared to many of the others I’m aware of, I don’t see any evolution at all. JavaScript is, at best, about spinning one’s wheels in place. It’s about the illusion of forward progress, while not actually making any at all. And that’s assuming it’s not actually yet another example of the frequent devolution we see with JavaScript. The change we see when using JavaScript and JavaScript libraries is not about letting us do more, or making our lives easier. It is change for no reason, and so often it is change for all of the wrong reasons!

    Reply
  5. alex

    > While I am at it, it would be very nice if GitHub made it possible to switch over the ownership of a project to another fork instead of continuing to promote the initial (but no longer maintained) version of that project.

    FWIW this can be done, you just have to email support and ask them.

    Reply
  6. Dan Abramov

    I am rather confused by your first point. If you used JSX and keep using JSX you did not need to change your code to “factories” at all. The change only affected consumers who used components as functions (e.g. MyButton(props) would need to be converted to createElement(MyButton, props)). If you keep using JSX you don’t need createElement at all.

    Reply
    1. Manuel

      Well I’m not quite sure about the theory, but in practice as long as I hadn’t done this change I would get deprecation warnings pointing me to that line, so I’m not entirely sure what was going on there.

      Reply
  7. Hans Thinh Le

    onClick doesn’t work anymore in 0.14.7 on iOS, that’s why after upgrading, I had to reverse to 13.3 again. Hope U guyz are aware of it.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *