Migrating from React to PureScript & vanilla DOM
Recently, I decided to drop React as my daily driver for building UI. It's easy to grab for tools like React, even when we don't really care for them, because they present a complete solution. When we drop React, we have to consider how we'll manage DOM updates, do front-end routing, organize our code, handle message passing, and so on. I think it's unfortunate that many development trends go this way: We grab for the tools that have nice support regardless of their philosophy because, at the end of the day, what we need is something that works.
Mixing HTML and JavaScript is really ugly and was probably never a good idea
Before we started doing this shit, we had nice looking templates ala Pug and similar. Of course, as always, there are hoops that we can jump through now to get something like Pug working over JSX, but it creates additional layers of complexity. How mature are those tools? What happens if the maintainer disappears? How difficult is it to configure those builds? How many hoops do I have to jump through to get syntax highlighting in my editor to work? Usually, simple, well-supported tools are what get the job done. So, the tendency was to drop all of our nice tools and embrace JSX. And I think most of us did this so that we could have:
- A way to encapsulate and compose components.
- Rendering optimizations from the virtual DOM.
- Declarative bindings that allow us to manipulate state via JS data rather than having to update the DOM "by hand."
The root issue with JSX is that the view's implementation has literally nothing to do with the component that it lives within. It's mildly convenient to keep a component's styles, view, and lifecycle together as a triplet, but when this comes at the cost of combining all three into a singular language (think Tailwind + JSX + JavaScript), the cost is much too high because it makes everything incredibly rigid. Now, instead of having three nice languages that are optimized for their respective domain, we have one Frankenstein that makes everything besides keeping that triplet together much harder.
React comes with too many caveats
The entire point of #3 up there is that we wanted things to be easy to reason about. But, when we start layering in references and effects, we go from "a nice declarative model that we don't have to think about" to "a quirky rendering lifecycle that we need to understand fully to get any real work done." And that quirky rendering lifecycle is, in many cases, quite a bit more complex than what we'd get from patching up the DOM ourselves with a little bit of help. In other words, point #3 never made sense more than superficially because React's abstractions attempt to make rendering simpler than it is at the cost of developer control.
React pushes everything to the frontend
This is more of a culture and workflow issue than a tooling issue. After all, there's nothing stopping us from building for the backend AND React if we want to. But, it tends to be the case that developers are comfortable and up to speed with a finite amount of tooling, so when everyone knows React, it tends to be the case that everything is built with React. I've found that this applies to my personal projects as well: I already have a system set up for React. Am I really going to think through another workflow just to build this one form on this one page that doesn't need React? Am I going to configure CSRF tokens and configure the backend view engine and make sure that all of my bundled static assets are available outside of the bundle and go refresh my memory on how Pug templates work (and update the dependencies) and configure appropriate template locals and so on? Or am I just going to grab React even though it's a little bit overkill for this form?
So, on a case by case basis, we tend to grab React over and over. But it comes at the cost of simpler workflows:
- Server side rendering tends to be simpler because we're closer to the data. On the server, we can just grab data out of the DB and render it into a template -- no load layer, no separate request lifecycle, no separate logic to capture form data and send it to the backend.
- Pages are lighter when served without an unnecessary bundle.
- When we render on the server directly, we don't end up circling back to try and get SSR through our client side technologies to support search engines and optimize rendering performance (which just adds one more layer to the abstraction shit-show).
- We get to use whatever languages we want.
- For simple websites, we can drop our bundler entirely.
I believe that we can do frontend development without a framework in a way that captures our original wish list using only Vanilla DOM manipulation, and this approach works quite a bit better than using a framework.
To be continued...