React is known for its relative lack of pitfalls and small API surface. This makes the few issues it has all the more frustrating, especially for the beginners.
I hope that these issues will be solved by more descriptive warnings in the next releases of React, but in the meantime here is something you, as a React beginner, should know in order to avoid hours of frustration.
Two Reacts Won’t Be Friends
While I’m happy that everybody seems to be converging on NPM in 2015 and NPM wants developers to use it for managing front-end dependencies, it still has rough edges. The biggest problem with using NPM for front-end dependencies is that if two packages specify a library like React as a dependency, they might get two separate copies of React. Even worse, they might get different versions of it. This works fine for something like Node, but not for browser libraries that want to mess with the same global mutable DOM! NPM tried to solve this with peerDependencies but all hell broke loose and they’re backing out.
In short, we don’t know a good solution to this yet, but if you’re having weird issues with different internal parts of React, consider checking whether you have two separate copies of React loaded on the same page.
Here’s just a few ways why this might have happened:
- You installed a package that specifies React as a dependency, and later installed React;
- You installed React, and later a package that specifies a different version of React as a dependency;
- You’re using a global React you get from CDN, but installed a library from NPM that got its own copy of React;
- You ran npm install inside some NPM dependency’s folder, thus installing its development dependencies that likely include React;
So how do you verify that this has happened? Easy! Assuming you have source maps (or Webpack’s devtool: ‘eval’) enabled, open Sources tab in Chrome DevTools. Press Cmd+O (Ctrl+O on Windows) and type “React.js”:
If you see two entries like on the screenshot above, delete the folder of the React copy you don’t need (in my case, that would be node_modules/react-router/node_modules/react). Now you can either ask your team to do the same and be careful in the future, or you can learn to use npm shrinkwrap to lock down the whole package tree so everyone on the team has the exact same tree regardless of package installation order. This takes more discipline but I’ve been happy with shrinkwrapping so far, as it prevents a lot of mistakes like this.
Don’t Render to Body
So what’s the other mistake I wanted to tell you about? It’s much simpler: don’t ever render to document.body. A lot of React examples do that because it’s less typing and looks more clear. Don’t. Do. This.
React wants to fully manage DOM tree under its control. If you append something foreign inside a DOM tree managed by React, it might seriously freak out, unless you do it very carefully (e.g. inside a leaf component that has shouldComponentUpdate returning false).
What’s the problem with <body>? Everybody updates it! Some people have non-React code that attaches modals to it. Google Font Loader will happily put <span> elements into body for a fraction of second, and your app will break horribly and inexplicably if it tries to update something on the top level during that time. Do you really know what all your third party scripts are doing? What about ads or that social network SDK?
Finally, consider something you have no control over: browser plugins. Yes, they can inject stuff into <body>. Of course they can also mess with the rest of the DOM, but at least you can fix the most widespread case.
So what do you do? Always put a root <div> into <body>, give it an ID and render into it. Another advantage of doing so is that you can put your scripts at the bottom of the <body> and you won’t need to wait for DOMContentLoaded before rendering.
Follow Dan Abramov on Twitter