We used to believe many things to be true. We believed the sun orbited the earth. We believed the plague was a punishment from the gods. We believed that Model-View-Controller architectures and two-way data binding were the best ways to structure user interface programs on the web.
All these beliefs were true to those who believed them at the time, as we didn’t yet have better ways to reason about the world.
Eventually, astronomers proved the heliocentric solar system, doctors discovered the microbial theory of disease, and React introduced us to the concept of unidirectional data flow.
None of these discoveries were immediately universally accepted. Galileo was tried for heresy. Dr. Semmelweis, who discovered the germ-based origin of diseases, was driven insane by his peers’ refusal to accept his research and died in an asylum. And still today, we haven’t yet agreed that not all UI architectures are created equal.
I strongly believe that React — or something like it — is the future of user interface development. But before you scroll to the end of the article to post an angry comment (or 👏 because you happen to agree), let me tell you why.
The forest, not the trees
React is a collection of clever ideas created by extremely clever people. When React was first announced, the selling point people used to talk about was its rendering approach — the idea that if we separate the representation of our application structure from the underlying rendered DOM, we can achieve a declarative view rendering syntax while still applying optimal DOM mutations.
If you’re not a web frontend developer, it’s really hard to understand how anyone can get upset over a language extension, but web is a weird place, and some people sure did.
Naturally, nobody likes being told their core beliefs are wrong. And so, among the blowback and the conceptual novelty, the core value of React still often gets forgotten.
React is a Component platform
React, at its core, is all about its components. The Virtual DOM is a cool piece of technology, but it’s only an implementation detail — if React could’ve been created without it, it wouldn’t need to even exist. JSX is a clever hack, but it’s only syntax sugar on top.
By now components are a well-established concept, and they did exist before React. The Web Components specification was already standardised when React was released. Much like Galileo wasn’t the first to discover heliocentricity, React didn’t invent components — it just found the right recipe for them.
React components are all about their composability and encapsulation. In addition, they benefit from being functional and multi-purpose. Let me break down what that means:
- Composability gives us the ability to build large programs from small programs using a model that doesn’t require one component to extend or depend on another — code can be written, modified, deleted and replaced independently of other code in the system, or even written before the future code exists.
- Encapsulation makes it possible to contain the entire presentation, behaviour and state of a component. This means that outside code can not influence its behaviour (other than by composing another component by wrapping it).
- Functional style of programming — props in, renderables out — makes React components robust and their behaviour easy to document and easy to understand, allowing developers to use components developed by others without needing to know how they work.
- Multi-purpose is just a fancy way of saying that a component can be either a container component (controller) responsible for data and behaviour, or a presentational component (view). This separation was always the key insight of MV* architectures, and the elegant React Component API allows true separation of concerns while retaining the desirable qualities of composability and encapsulation.
This small and focused component abstraction gives us all the qualities we as developers seek: it’s easy to learn and remember, ergonomic to use, and results in programs that are possible to maintain and collaborate on.
But there is one more quality of components that I haven’t talked about yet, and that, it turns out, is the most important one.
Components are an Innovation Primitive
I began this article by praising the virtues of React, but I’m happy to admit React is not perfect; there are still tons of missed optimisation opportunities that the React core team is actively working on, and new ideas are constantly discussed, implemented, and discarded. When you look at the larger ecosystem of libraries, patterns and practices around React, they’re even further from perfect. We can’t even decide if composition should be done with Higher-Order Components, Render Props, Functional Children Props, or, god forbid, with Component Components!
While the wealth of options can sometimes be fatiguing, it is in fact a perfect example of the true strength of React’s component model. By having a universal, small, composable, flexible and reliable building block, we are able to discuss, test and accept new ideas without having to discard all our existing work.
It’s absolutely crazy to me that individual developers on different sides of the world can build revolutionary libraries like Redux, Apollo or styled-components, and application developers can adopt each of these new tools alongside their existing toolset without having to modify their existing code, or replace each individual piece without having to throw the baby out with the bathwater.
Each of these libraries do complicated things outside the React sphere, but none of them step on each others’ toes, because each of these tools can take advantage of the component composition model. Thus, the React component model is not only a user interface primitive — it’s an innovation primitive.
Change is the only constant
Having a platform that adapts to change is paramount, because change is inevitable and revolutions are expensive.
Smart people won’t suddenly stop seeing problems in the world and coming up with solutions for them. People won’t stop being frustrated by the constraints that exist, and won’t stop trying to break free. We as a developer community won’t suddenly stop wanting to adopt better tools when they are discovered. No matter how hard you want to bury your head in the sand and hope things stay the way they are, progress can’t be stopped.
When change happens, the best you can hope of a system is that it can adapt to change, because the only alternative is an unthinkable tragedy.
After the framework wars of the last decade, we now live in a time of unprecedented productivity, prosperity and peace. Tools like React allow us to build and maintain ambitious products.
If our tools stood in the way of progress, we would eventually have to discard them to move forward. And the cost of revolution is ruination: we would have to replace all our libraries, relearn all our good practices, retrain all our teams, repeat all our mistakes, and rewrite or refactor an incredible amount of code, instead of focusing on the products we were creating.
Revolutions are inevitable
Innovation moves at two speeds:
- Fast revolutionary bursts
- Long tails of incremental refinement
React was a revolution. It overthrew the status quo of best practices of its time and provided us with a significant improvement in developer experience almost overnight. Now we’re fine tuning the engine until we can no longer squeeze more juice out of it. The refinement phase of the innovation cycle will eventually plateau, and will no longer be able to demonstrate the year-on-year progress that we’ve become accustomed to.
But the curve is not a fixed function. We can keep innovating and creating, as long as we as developers make decisions that keep our ecosystem responsive to change.
Let’s write software that’s responsive to change
No matter whether we’re working inside the React ecosystem, using another framework, building frameworkless progressive web applications, or even building software not related to the web platform at all, we can follow these lessons learned from React and its ecosystem to make the code bend instead of snap when faced with change.
- Build big things from small things — compose software from smaller well-defined units, avoid monoliths. Carefully consider APIs and interfaces so that they can be used by others.
- Make code easy to copy and delete, not easy to change — prefer composition over inheritance and mixins. Encapsulate behaviour. Do one thing and do it well.
- Write code for humans first — make it possible to understand your work. Write code that expresses intent, not methodology. Name, document and catalogue your code so that everyone else can use it.
- Don’t break things if you don’t have to — Stability is a value unto itself, because change causes work and waste, or in the worst case, an AngularJS-level extinction event. Deprecate before breaking, use code mods or similar tools to make adapting to change easy. In your own code, don’t adopt new libraries just for the sake of it — measure cost vs. benefit before you do.
- Keep an open mind — The final rule is a corollary to the previous. Sometimes new ideas can be scary and change can be inconvenient, but occasionally a revolution is necessary for change to take place. Don’t become a best practices police — keep an open mind and listen to what people are saying.
React is the winning ticket (for now)
I believe React is the obvious choice for building ambitious user interfaces that will be able to stand the vagaries of progress.
Eventually, React will be replaced by something else. We don’t yet know what the forcing function will be — WebAssembly, perhaps — and we don’t know what comes next. Whatever it is, I look forward to it with a mix of trepidation and excitement.
For now, thanks to React’s resilience, I’m confident we will get to enjoy our period of peace, prosperity and productivity for a while longer.
If you want to read more of my musings at the intersection of technology and comedy, follow me on Twitter.