R for React (NERP stack part 3)
2016 Feb 15
Remember, NERP is:
First, some terminology. React is frequently used in the same breath as Angular, Ember, Knockout, Backbone, and other MVC web frameworks. But the truth is that React can’t be compared directly with these frameworks, as it says on the React homepage:
“Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.” (https://facebook.github.io/react/)
So React isn’t a complete solution by itself. You give it data and it renders or re-renders HTML. And will emit events on user interaction. But that’s it. So, when I talk about React, I assume that you’ve selected a Flux implementation to go along with it.
You may have heard this nebulous term in connection with React. In a phrase, it is the data flow for your application. When the user makes a change in a textbox, where does that new data go? How does overall application state change? What exactly is the overall application state?
The old model (jQuery and most frameworks), is to make some local UI change to the DOM then emit an event. Other components listening for that event can make their local DOM changes, then emit yet more events, and so on. In a large app, it gets hard to track the cascade of changes both to DOM and application state. Especially since they can easily get out of sync!
Flux dictates that the user’s change must first affect the central application state, before attempting to do anything with that change locally. When application state changes, the app re-renders, finally changing the DOM. Cascading data changes are explicitly disallowed, greatly simplifying already-complex large applications.
This means that all application state is entirely separate from the DOM. On save of user settings, for example, we don’t extract data from the form on the page - we use the current application state. Want to know what the UI should look like? Just look at the current application state.
I has a very constrained data flow. Small objects (actions) are passed through a central chain of middleware functions (not the same as Express middleware) which can modify those actions, then the actions are passed through a set of reducer functions which are the only thing which can change the central store. Finally, when the store changes, your React view components are re-rendered with its new data.
It’s the simplicity, and ultimately the restrictions, inherent to this architecture which make it worth using. Extremely useful functionality can be built on top of it:
- Easily-implemented undo
- Dev tools showing every change to application state
- Saving complete application state to localStorage
- Copying all application state from one browser tab to another
- Implementing HTTP calls declaratively
R for React
So now we know a little bit more about what React provides, and what people generally mean when they talk about it. Should we use it?
If some of the stuff above already seemed pretty great to you, there’s more!
- Just re-render!
- Build-time errors with JSX
- Flexible rendering
- You will conform!
1. Just re-render!
All that goes away with React. Instead of direct DOM manipulation, your
render() functions produce an intermediate data structure which represents the DOM as you’d like it to look. React diffs that with the existing DOM, and then makes only the necessary changes. Your app no longer has to think about synchronizing DOM state and application state.
This simplicity allows initial naive development, no or minimal performance concerns. Most of the time, this is fine. When performance optimizations are later needed, the tools are available. Re-use of DOM nodes via the
key prop can provide a nice boost in some scenarios. And you can even do a little bit of extra work to tell React that a given subtree of your component graph doesn’t need a re-render at all.
2. Build-time errors with JSX
Developing for the web has long required near-constant refreshes of your HTML page. It made sense; you couldn’t really tell if your markup was well-formed until it hit the browser. This got worse with a framework like Angular or Ember: now you load the page, which loads the library, and then the library processes the HTML to find your directives. Typo in your directive name? Malformed loop syntax? The only way to know is to try it! Click the button and see if your logic fires!
Now, with JSX, a missing or mismatched
React.createElement(Tag). It makes the whole thing easier to reason about.
Beyond that, basic unit testing of your React components gives you more confidence than with Ember or Angular, because you’re not missing the markup/directives half of your functionality!
Of course, there are still many scenarios where loading the page is required. Here React provides specific friendly error messages in development mode. And when you use a React component, the component’s specified
propTypes can warn you about missing or malformed props.
3. Flexible rendering
Your first experience with React will likely be in the browser - an initial render, and then a lot of small updates the DOM as the user interacts with the application. But React has two key additional forms of rendering available:
So, what we need for mobile (and performance in general) are smaller downloads, faster boot-up time, and intelligent use of resources. React has a smaller download and faster boot-up versus other frameworks. Take a look at the numbers - there’s a clear difference (via this gist):
|Angular 2 + RxJS||143K||766K|
|React 0.14.5 + React DOM + Redux||42K||139K|
|React 0.14.5 + React DOM||40K||133K|
5. You will conform!
As we explored above, React has a very specific way of doing things. There are different Flux implementations, but the core data flow is the same. This consistency reduces the number of architecture decisions required for a given project, and decreases ramp-up time for a developer who has previously used React.
Additionally, everyone I’ve spoken to says that this is not just another hot library - it’s a radical rethinking of the way user interface is done. And it doesn’t just feel warm and fuzzy. When features are reimplemented with Flux, longstanding tricky bugs just go away.
As always we need to temper all the excitement with a little bit of reality.
- You will conform!
- Breaking changes
- Batteries not included
- It’s new
1. You will conform!
Yes, it is a double-edged sword. It’s not necessarily easy to re-train yourself in the way of React. The longer you’ve been writing web applications, the harder it is wrap your mind around it all.
The old is gone - you can no longer sprinkle jQuery around for the odd tricky situation. And the new is uncomfortable. When I last taught Flux, my students struggled with how much code it took just to update one textbox. I didn’t like JSX when I first encountered it.
Once you’ve internalized all that, it’s time to start thinking about your existing functionality and third-party libraries. What do you reimplement, what do you keep in place? You’ll need to pay particular attention to anything that attempts to modify the DOM itself. React is very jealous about the parts of the DOM it manages, but you can explicitly tell it to back off.
2. Breaking changes
React is not yet at version one. I’ll say it again - all version numbers still start with zero! In the world of semver, that means breaking changes can happen at any time. And they do. For example, with the recent release of 0.14, React split in two: the core
react library and the web-specific
react-dom library. Big changes like this are still happening, because the project is evolving quickly.
react-native was first released less than a year ago!
The good news is that React is being used in production by Facebook. And not as a toy. They claim that they have 15,000 React components in their codebase. Therefore, the community understands the cost of breaking API changes, and does its best to help out. Deprecation warnings show up in versions prior to the change, and when the changes are in place, so are codemods, which can help migrate a codebase.
3. Batteries not included
Angular comes with a component for making AJAX requests, React doesn’t. Angular comes with an implementation of promises, React doesn’t. Angular has logging, React doesn’t. Angular has built-in routing. React doesn’t. Yes, a Flux implementation gets you closer to a web application framework, but you’ll still need to select your preferred library for all of these scenarios.
But that’s nothing new in the world of NERP, right? By now you’ve started to realize that the NERP stack is not all you need, it’s the backbone of the app you end up building - a place to start. You have the power to round out your application with whatever it needs: heavyweight or lightweight solutions.
4. It’s new
React was first released a little less than three years ago. It only broke through to the mainstream in 2015, and its search volumes still lag behind angular. Can you trust it? Will it just go away? Will you be able to find developers who know it well?
It seems to be getting more popular every day, but we can’t be sure. There is one thing we do know: React will eventually be replaced with the next hot thing. You’ll have to decide if you can get enough value out of it before then.
Conclusion: Jump in!
React is an innovative tool for building user interfaces in the browser, on the server, and on mobile devices. It’s not another framework that tries to provide the most value out of the box, or twist itself to work the way you work. You will conform!
But, like a strict personal trainer who has your best interests at heart, your applications will end up better off. Instead of spending time on syncing application state and the DOM, you can focus on features!
Full NERP ahead!
R is looking great! You can feel the momentum building - just one letter left to complete this new technology stack! Surely it will become one of the classics, known by all even if only the the lucky few can fully embrace it!
Next up: Part 4: P for Postgres…
Fun fact: As of Feb 2016, this blog is statically generated with Node.js and React/React-Router.