Sunday, June 16, 2024

Adopting React in the Early Days

Programming LanguageAdopting React in the Early Days


Intro

When ReactJS was first announced in 2013, it was not an instant success.

At least, I personally took a glance and dismissed it quickly. I finally changed my mind just a few months later. I’ve now been using React for over 9 years and even created a popular newsletter called This Week In React.

But adopting React hasn’t always been so easy. This article is not about the story of React, but rather a tale of my own experience of the early days, and the challenges I encountered.

Personal Context

To understand my perspective, it’s important to give you some background.

Early in my career, I was a typical corporate Java JEE backend developer, using technologies like Spring, Hibernate, and dependency injection. After 3 years of experience on that stack, my goal was to grow my technical skills fast to have a more attractive developer profile. In 2012, I started contributing to a project for free, in my spare time, using all the newest technologies: Scala, MongoDB, ElasticSearch. This project later became a startup. I quit my job and became the CTO from 2013 to 2017. My position gave me the opportunity to assess React as soon as it came out.

Giving React a Second Chance

As a startup CTO, I wasn’t even a full-stack developer. I would even say I didn’t like JavaScript much at that time. My coworkers were working on our Backbone.js frontend app but struggled with some architectural concerns. Everything was tightly coupled, and it became hard to add new features without introducing bugs. It was time for a big rewrite, and for me to ramp up on the frontend to ensure this doesn’t happen again.

Even though React didn’t make the best first impression on me, Pete Hunt and a few others have been great ReactJS advocates (Rethinking best practices). We were willing to give ReactJS a second chance. At the end of 2013, we created 2 proof of concepts: one with AngularJS and one with ReactJS.

I worked on the AngularJS proof-of-concept. It was the natural, less risky choice for me. It was popular, full-featured, had a large community, and was appealing to many corporate Java developers due to its usage of dependency injection.

My co-CTO did the POC in ReactJS. He was a good functional programmer and tried to convince me of the superior model of ReactJS. It started to make sense to me: I really liked the idea of View = f(State).

Then David Nolen published The Future of JavaScript MVC Frameworks. For the very first time, someone explained how immutable data structures could be useful and even optimize frontend applications. This really made sense for both of us, and we saw ourselves leveraging our functional programming skills on both the backend and the frontend. The Om framework was in ClojureScript, but we didn’t really want to use that language for various reasons.

Learn more about the early days of React.js. Discover how it all began.
The 6 best frontend communities to join.

Adopting React – Not Without Challenges

In January 2014, we started our app rewrite on React 0.5.2.

At that time, we had to create components with a createClass API:

React1

It was already possible to share reusable React behaviors thanks to mixins and lifecycle methods.

React2

This worked fine but occasionally you could encounter naming conflicts because the same variable name was used by 2 different mixins. Later, hooks somehow became the more composable successors of mixins.

React3

There was no React.memo either, nor React.PureComponent, and we had to optimize React re-renders by hand, implementing a shouldComponentUpdate method.

React4

Initially, React didn’t bias too much toward functional programming, assuming developers may mutate state and call this.setState to tell React to re-render manually. But if you avoided mutating state, it was possible to use a similar optimization to the one everybody uses today:

React5

There was even an official mixin for that:

React6

There was also no context API. At least, not officially. But there was this undocumented (at that time) legacy context API that we were advised not to use.

React7

Also, there was no leading state management solution. At that time, we only had React local state, and everyone was trying to create their own state manager using the secret “do_not_use_or_you_fill_be_fired” legacy context above to solve the elephant in the room: props drilling. Using this API directly in your app remained uncommon, but it has proved to be successful and easier to use behind an abstraction.

Facebook introduced Flux. That architectural pattern looked interesting, but the proposed implementation looked a bit awkward and the community didn’t settle on it. Many pointed out the relationship it had with backend concepts like Event-Sourcing.

Facebook also introduced Immutable-JS, immutable persistent data structures for JavaScript, inspired by ClojureScript, Scala, or Haskell. It looked like a great idea initially, and many tried to use it. Over time the community figured out that the cost of using these non-native immutable collections in JavaScript outweighs the benefits. List virtualization became a more popular solution than Vector Tries to solve performance problems. Note we may get native immutable data structures in JavaScript someday: Records and Tuples could have a great impact on React in the future.

The community took inspiration from Flux, Om, and functional programming concepts to create a myriad of state management solutions. It wasn’t so easy to make a choice between Fluxxor, Flummox, Morearty, React-Cursor, Omniscient, Baobab, and many others. I am also guilty of creating my own Atom-React package, but still proud it had an early prototype of time-travel debugging before Redux.

Many really liked the idea of having a single global state object and tried to avoid local setState calls. However, it quickly became clear that being too dogmatic in how you manage the state doesn’t lead to great outcomes. 

Rendering your global state from the very top didn’t scale very well. It led to maintainability issues due to props drilling, but also performance problems. Re-rendering your whole app whenever you type a single letter on a text input was quite expensive and made the app less responsive, even when carefully using shouldComponentUpdate on all the expensive components. It was clear that we needed something better to allow connecting a global state to deeper React components and avoid re-rendering from the app root. The usage of the legacy context was quite common through React mixins. 

The Advent of Redux

When Dan Abramov introduced Redux at React-Europe 2015, the community was quick to acknowledge it was the cleanest implementation of Flux out there, and also leveraged functional programming concepts like immutability and event-sourcing in an elegant way. Many of us deprecated our state management libraries and adopted Redux instead. 

Redux significantly helped make many functional programming concepts mainstream and popularized the concept of the reducer, which later made its official apparition in React as a hook. Redux also greatly popularized the idea of Higher Order Components through its connect function as an alternative to mixins to solve the props drilling problem.

Redux also set the foundations to create a whole ecosystem. The Redux DevTools was a good demonstration of something powerful you could build on top of it. This drove a new wave of innovations leading to the creation of many other interesting projects such as Reselect or Redux-Saga.

Whether you like it or not, Redux made history and federated the community. It was the first library to provide an elegant solution to many problems that all React developers faced.

The Way React is Designed

Comparing React to Angular, the 2 frameworks have very different stories and design philosophies. 

Angular came out as a full-featured framework with strong opinions and best practices. Over time, this led to some poor initial design decisions due to the lack of pragmatic feedback and various annoying breaking changes. The innovation mostly happened within a small circle of developers instead of appearing organically in the broad community.

React took a very different approach. It didn’t try to solve all problems at once and let the innovations happen in userland. Yes, it was hard in the beginning, but retrospectively it was the right thing to do. Facebook could have pushed to add Flux, ImmutableJS, and other opinionated patterns to React core. Instead, they let our community take ownership and figure out collaboratively the higher-level primitives we wanted to work with. 

The way React is designed over time reminds me a lot of the Extensible Web Manifesto. This document which is well worth reading explains how the web aims to be designed to prevent irreversible mistakes in web and browser API design.

I’d also like to give an honorable mention to escape hatches. From the first React version I tried to nowadays, it has always been easy to integrate React into an existing app or to integrate an existing JavaScript library. Back in the day, it was common to add React as a view layer to a Backbone app or to use JQuery plugins and fancy layout libraries like Masonry.

Conclusion

Adopting React was challenging in the early days. There were no clear solutions to many common problems. Some APIs were less elegant than the ones we use today and take for granted. Our startup had to innovate, and we created our own “temporary” state management solution until the community settled on Redux.

Make sure to watch Honeypot’s React Documentary if you want to know more about the early days.

Overall, the React programming model hasn’t changed much over the years. Many old concepts still exist today in different forms.

Over the year, the React team has done a great job focusing on low-level primitives and good escape hatches first, allowing a good level of innovation to happen in userland.

Back to the future, we now talk about Concurrent Features, React 18, Suspense, streaming, and Server Components. New low-level primitives have landed! It’s time again for our community to step in, innovate, and build on these primitives in ways that even the core React team hasn’t thought of.

The next few years will be exciting. You can count on me to tell you about all the latest React innovations, through my newsletter This Week In React.



Check out our other content

Check out other tags:

Most Popular Articles