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
The upgrade path
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
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.
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
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:
[Error] Warning: validateDOMNesting(...): <p> cannot appear as a descendant of <p>. See BookTeeTimeModal > p > ... > p.
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
Warning: validateDOMNesting(...): <tr> cannot appear as a child of <table>. See Buddies > table > tr. Add a <tbody> to your code to match the DOM tree generated by the browser
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).
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.
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.
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?