posted under category: General on January 4, 2023 by Nathan
I don't apologize for the clickbait title. I've worked on the web for over 25 years now. I've seen a lot of things come and go. I've seen good technology and bad, both win and lose.
Over the past handful of years, I've become less and less of a fan of React. The more I think about it, the more I believe that React is the worst popular framework (sorry, "LIBRARY") that exists. Let me explain why.
A year or two later, I found Vue.js, which actually solved all of my problems with React at the time. The following year, I was handed another project written in React. It was a SPA. I studied and learned, and I expanded it and helped stabilize what we had built, experimented with it a lot, pushed it to its limits, decided what I liked and did not like, became a company expert in React, and then realized that so much of it was too brittle to change and grow in a meaningful way, plus I... had the time... so I rewrote it in Vue - it took 7 days (even though thought I could do it in 5). Everyone loved the result.
The next year, I found myself with another React app, this time a much larger app with a team of developers and years of history. At first I was happy to nurse it along and add features until I got reassigned somewhere else, but then I started to notice the underlying issues. This one had serious architectural problems, reactivity problems, complexity problems, and performance problems, plus new features that would not be easy to add on. We decided to rewrite it, and I pushed my preference for Vue.js on the team. It took 6-9 months, but we made something scalable and capable that is much easier to work with. I will admit that most of my interactions with React have been bad applications, so that's where my rant really begins.
1. React is bad by default. A successful React application must be backed by very experienced developers. Newcomers aren't likely to fall into the "pit of success" - it's more like mountain climbing - you have to prepare, you have to bring a guide, you have to study your approach step-by-step. If this is your first time on the mountain, you won't summit.
2. React has a doctorate-level vocabularity. "Immutability" "Memoize" "hydrate" "Pure Component" "UseEffect" "Synthetic Event"
These are words come directly out of a DCS degree program but don't make sense to non-doctoral, even native, English speakers. It's not that this is hard to understand these words and concepts, it's that it's unfriendly - hostile even.
3. The Lifecycle Event vocabulary is just stupid. I'm glad that we have mostly done away with some classics like
ComponentWillReceiveProps which was infinitely confusing, but
getDreivedStateFromProps - what are we doing here? To contrast, Vue.js simply has
updated, and so on, while Svelte has
4. Hooks are yet another brainiac term that are contextually unclear. Hooks somehow simultaneously do everything and nothing. They replace state and they are the new event system and they are the easier way to cache things and they are the best way to debug everything. But how do they work? And why? Only little explanation is given.
5. Why do hooks have to be the first thing in a component? This feels like a code smell that indicates poor design.
We're going to hear this a lot: Let's compare it to Vue. Vue implemented a hooks system, but they took out the odd placement requirement - you can put them anywhere in Vue.
6. When we add Redux, we add more vocabulary problems, specifically the way Redux redefines words incorrectly, for example
Action- A tiny data structure that doesn't do anything (not an actual action)
Reducer- unintuitively, changes the state (immutably)
Dispatcher- a useless convention to do a switch/case with actions for some reason
7. When we use Redux, or any state mangement library that loosely follows the Flux pattern, immutable state creates many copies of our state in-memory. A lot of state changes at one time will run up our memory usage very quickly and cause additional garbage collection, which may happen at any time, and can affect the performance of our applications.
While an immutable state avoids a number of problems, and may solve some very complex concurrent data issues, a large immutable state is a problem on its own.
8. If we added Redux to an app before 2020, we absolutely need to have added Thunk and Saga for asynchronous operations, then Reselect to "memoize" (or, cache) the Selects from the global state, plus Immer to shrink our reducers' scope. None of this is documented of course. None of this is official. You'll only see it if you read the right blog on the right day, but this information is basically required if you want your app to work the way you expect it to.
9. If we added Redux after 2020, we would absolutely need Redux Toolkit (unless you're a sadist), which of course has its own additional vocabulary.
Slice- sort of a module of the state with selects and reducers all-in-one
EntityAdapter, I'm not even sure what these are
Redux toolkit makes Redux a lot more manageable but adds more overhead, both physically (in bytes) and mentally.
10. Redux, and all flux-patterned global state management systems add an incredible amount of complexity.
Complexity adds bugs, so this is a simple formula: reduce complexity to remove bugs. Remove Redux to reduce complexity. Therefore, remove Redux to make better applications.
But of course we need some kind of global state management. Therefore, we should all be looking for the simplest solution to reduce our bugs. Even though Redux is the most-default-looking choice, we should shop around. Instead of Redux, try MobX, or Recoil, XState, Hookstate, Akita, ClearX, Rematch, or any of the, roughly, 50 different great choices available.
11. And that brings us the problem of choice. React has no default path to success. There are no "easy" decisions. Every choice presents fifteen options, none of them advantageous over the others; every library needs seven more; each choice Fractally branches out to infinity, with options forever.
12. React is bloated.
- If you add React + React Router + Redux + (reselect+thunk+immer) + React DOM, you get about 300kb of framework files
- If you do the same for Vue, which BTW is just Vue + Vue Router + Vuex, it's only 100kb
- If you do it for Svelte, it's essentially 0kb (not in reality, but Svelte is like a kind of magic)
13. React carries around features that only Facebook wanted to add. For instance, no one else was interested in...
- "Concurrent Mode" - billed as interruptable UI re-rendering
- "Portals" - Which I think finally went into production; it renders a component somewhere else in the page
- "Transitions" - Which are supposed to help with loading new content
- and "Suspense" - Which creates a framework-preferred way of building a "loading" state for components
Don't freak out, React is still fast and it does a lot of things well. It just isn't the fastest, most powerful, most scalable thing out there. Not by a long shot.
15. React-DOM is a separate library. Imagine me, a veteran web developer, attempting to add React to a web page. Yeah. It turns out React doesn't do anything unless it can interact with the DOM through a second library that weighs in at 20x larger than React.
I do understand why it's separate - it's because the external-facing parts can be swapped out to interact with something that's not a web page, such as a mobile app. That doesn't excuse the fact that this is confusing, unintuitive, and probably not the best solution possible.
The reality is, we also need Webpack to bundle and serve our React app, and the easiest way to do that is through the React CLI. To contrast this, by the way, we can add Vue to a web page with a script tag like jQuery and just start using it.
17. The React CLI is powerful, but limited, and breaking out of those limitations requires us to eject our applications from the CLI. Ejecting from the React CLI is a one-way operation, and it leaves us with a 2MB (or more) webpack.config file - these are usually around 1-20kb - this 2mb size is unmaintainable and impossible to work with. While it could be a testimony to how much React CLI is doing for me under the hood, the reality is that it's inconsiderate.
18. Code-splitting is a nightmare. Who of us have attempted to split our webpack chunks with React? Code splitting is an amazing performance enhancement that can cut down the initial download and processing our users have to go through, letting them download the rest of the app as they explore it. And for some reason, this was hellacious for me, multiple times, in multiple applications, over multiple years. This caused big problems trying to make my React applications more scalable. Again compared to Vue: Vue makes this trivial.
19. With React, you either choose JSX or pain. No one in their right mind would code an application with
React.createElement() instead of JSX. A lot of the benefit of React is the way that JSX integrates. JSX is the default templating engine, the only templating engine, and there are no other choices. It feels like an OK thing because JSX is passable, but it also feels a little bit like vendor lock-in. Switching templating engines is not something that people do in React, however it's something we can do in Vue, and in a lot of other frameworks.
20. The reason I bring up our inability to switch from JSX is because JSX sucks. For instance:
- To add a CSS class to an HTML element, we have to call it
className=""- this is the cause of a lot of errors I've seen (and caused).
- Similarly, the
forelement is illegal, we have to call it
htmlFor- it's ridiculous to me that HTML is invalid by default.
- JSX is XML. I thought we fought the good fight and squashed most of the XML in the world, but here we are again.
- CSS with JSX is such a problem that there are 63 CSS-in-JS frameworks for React!
21. React state properties are not really "reactive." When we change the state through an official means, like
setState or through a
useState hook, React re-builds the entire component, and potentially the component tree. Of course virtual DOM will keep the screen painting as small as possible, but still component re-rendering can be expensive. Instead of saying that properties are reactive, I would say that state changes in React are overreactive. Again, when we compare this to Vue, every state change is immediately, minimally, and truly, reactive.
22. In 2017, a potential change to the React "OSS" license could have allowed Facebook to revoke the ability to use React to anyone they wanted; imagine writing 6 million lines of code in React and then being told we can't use it anymore because someone violated their policy. That's a game over. It's a bankruptcy waiting to happen. Of course FB recanted the whole idea, but there's still a strange potential for something like this, isn't there?
23. The Other Facebook Dilemma - if Facebook wants to change something in React, they just will. Companies - even huge ones like the one where I work - have no say in the matter. Conversely, if you need a feature changed in React, it's not likely to happen.
If you need a feature changed in a smaller ecosystem like Vue or Svelte, I guarantee that a healthy donation can move a lot of code.
I think we all owe it to ourselves, and especially to all the new programmers out there, to stop choosing React as the default. There are plenty of occasions where it may be a good choice, but I don't think it's very often.
What do you think? I bet that if you've been a fan of React, this is pretty inflammatory, isn't it? I admit, I probably don't know as much as you do on the subject.
On the other hand, you may be sick of seeing React everywhere like me. In that case, you're probably thinking of a few more points I could have brought up.
Whether you're seething with rage or want to pat me on the back, feel free to drop a comment or tweet me back. I'm looking forward to hearing from you.