Facebook rewrote most of React and released version 16. React 16 was a highly anticipated update, especially in view of the new Fiber rendering method, which greatly improves performance. The React development team in the latest version has diligently marked methods and packages as obsolete (deprecated), and we saw their warnings in the console. In fact, migration is not so easy for a large project.
We in Discord have just launched an update of our application based on React 16 and want to share our experience that we received during the migration.
Why did we do this?
The Fiber architecture looks like it can greatly improve the smoothness and speed of both the rendering of the DOM tree and the Native components by breaking the rendering process into components, giving preference to the faster parts. Right now it’s not very clear whether these benefits are significant (for example, asynchronous rendering is still not included), but these changes are paving the way to a bright future.
')
Ultimately, you
will have to upgrade. React is in motion, and the 16+ community will surely leave you abandoned alone with bugs. And as a bonus, React 16 brings new features, for example, ErrorBoundary.
There are many changes to it that you will have to get used to.
Hemorrhoids starts here
Like all migrations, the level of pain will directly correlate with the age of your codebase. Facebook has done a good job over the past months, flagging deprecated-methods, but not all libraries are well developed, and if you depend on such libraries or on the libraries that you fork, you will not have a sweet time.
Your team will also have to decide whether it is time to fully switch to ES6 classes or functional components. We
strongly recommend doing this
after a successful migration, so that you can understand what has broken (or even better, by covering up with tests) before you update the project.
We wrote quality code, but this was not enough to easily switch from mixins and React.createClass. We also used specific versions of the packages, which gave us stability and confidence, but our dependencies became old and covered with moss.
Here we are faced with the pain of migration.
Run the codemod
First of all, you must use the codemod to move from React.PropTypes to prop-types, and migrate from React.createClass. Codemod helps you to make the right choice: use the class Component where possible (or PureComponent in the case of a simple mixin), and where to use create-react-class.
Install
jscodeshift , and clone the react-codemod
repository . Then run the following commands for your project:
1. Transition to prop-types
jscodeshift -t ./react-codemod/transforms/React-PropTypes-to-prop-types.js /path/to/your/repo
2. Switch to Component, PureComponent or createReactClass:
jscodeshift -t ./react-codemod/transforms/class.js --mixin-module-name=react-addons-pure-render-mixin --flow=true --pure-component=true --remove-runtime-proptypes=false /path/to/your/repo
The last is to follow the well-described
tips in the react-codemod repository. The most important of them are:
- Switch to ES6-classes, if there are no mixins, and to PureComponent, in the case of simple mixins.
- Ensure that if the created classes contain all static parameters.
- Created annotations from React.propTypes.
- Moved context binding
this.method.bind(this)
methods from constructors to switch functions (unless, of course, you like it). - All components that use the outdated API are missed, for example, the
isMounted()
method, and so on.
Codemod will find a lot of errors in the project that need to be recorded and then corrected manually.
If you do not like this auto-change, you can manually correct anything to your taste. Codemod is a great tool, but not a magic wand. He does not migrate the project completely for you!
Search and replace
We found many places in the code that the codemod missed. Apparently, where we imported and called PropTypes directly. Most of the manual fixes had to look for calls to the
isMounted()
method and create the _isMounted property, which was
true
inside the
componentDidMount
and
false
inside the
componentWillUnmount
(
but maybe you shouldn’t do that )
Denial of Private API
Of course, you do not use the internal API of the React framework, right? Those that are in
react/lib/*
. If you rely on them in your project, then the bad news for you: you are out of the box.
Some APIs have been moved to external libraries (“react-addons-” package family), and some have been removed altogether.
In our project, we relied on the samopisny TransitionGroup, which depended on the flattenChildren functions from the
react/lib
. But now there is no such function, as well as childMapping and mergeCildMapping. In this case, the developers of the framework recommend simply to take and copy these missing functions. This we finally did. However, we have built in new versions of those functions from the react-transition package.
Thus, we are faced with the first serious problem. But our application still did not work.
Dependency update
This item turned out to be one of the most tedious. And if you do not have a fever to upgrade to version 16, it will be wise to wait until most of the libraries are updated. If you want to upgrade, it will help everyone.
The console still shows warnings about packages that use React.PropTypes or React.createClass. You will need to upgrade these packages. Either you have to replace them, or somehow get around, writing your decision. Or, fork and fix them if the packets seem to be abandoned.
The main problems that need to be solved:
- React.PropTypes
- React.createClass
- Dependencies on the interns of the framework version is less than 16
Unfortunately, not all errors are easy to find, and here lies the real hemorrhoids.
Faced with an error, it took two whole days to search for the required library. And you will have the same thing. The console kept telling us that the reactRiberChild cannot provide an element because the
ref
attribute was
undefined
. We tore the hair on our head, looking for what could have gone wrong, and eventually dug inside the React code, where we saw the cause of the error. It turned out that the 16th version of React does not like
ref: undefined
, but
null
is quite ok. After that, it became clear that the elements created using one external library had the
ref: undefined
attribute. After we forked the library, modifying its code, everything worked fine.
Particular hemorrhoids
Well, we also found subtle errors. Again, in most cases, a transition to new versions of libraries or their modification was required. It often happens that when you update a fairly old library, it changes the behavior of the elements, and we get full of stitches in our beautifully written CSS rules.
Most libraries only fixed their problems with the warnings in React 15. And only their most recent versions will work in React 16. This is a tedious job of finding and detecting errors.
Switching to ES6-classes means that you now need to be careful not to change the props attribute. Previously, it was just a warning to the console about this, now it falls with the error “Cannot assign property read XXX of object '#'”. If you are destructuring the props attribute and trying to change its properties, this error will hit you. Instead, use Object.assign or its syntactic sugar:
const newProp = {...prop, propertyToOverride: overrideValue}
Also, React 16 will warn you that the bolean value is received from the onClick method. This will happen if you write
onClick={!disabled && this.handleOnClick}
Therefore, it is necessary to control the behavior within the assigned function.
And here comes React Native
We still have an iOs application that shares stores, utils, and some action creators with a web application. Switching to React 16 also motivated us to use the latest release of React Native, which depends on the alpha version of React 16.
Unfortunately, we need special timers for the video, and we had a fork of React Native, and we returned to version 0.34. React 16 compatibility came to React Native only from version 0.48.0, so this is the time to upgrade ...
This turned out to be especially painful. The biggest headache was that we used the React Native Webpack Server, which allowed us to reuse the code between our projects. So we revised our views on the use of the collector and began to switch to
Haul .
Haul keeps us still in the Webpack universe, but it was not so easy to tune it into an existing project. The story about this pulls on a separate article.
We used to import images for iOs using the
require('image!image_src')
, but now it’s not possible to use it in new versions of React Native. It was a grueling job - we needed to move the images and change the way we accessed them. Even after using the codemod, this part of the work was done mostly manually.
We also found a lot of obsolete classes, and now we recommend using a crutch in the form of
react-native-deprecated-custom-components . You will need the latest version since they recently updated the React 16 package.
We take in work new features
The best innovation for us was the new
ErrorBoundary . React 16 now stops rendering the tree in case of an error (which is good), and you will be able to render some kind of error message. Additionally, the restrictions provide a good opportunity to have a separate place for logging well-described errors with information about the call stack and components.
It started!
So now it works, and the user experience has become full of excitement and satisfaction? Not. We found that it did not work faster. But it will, because the new architecture gives us new opportunities.
The biggest advantage when playing long. Our code has been updated, which allowed us to normalize most of the code, revising some of the basic tools. This will have a good effect on future upgrades.
Performance is a nice bonus.