Overcoming Popular Issues With React ProjectsPublished on
This conversation started on a Hacker News thread.
I want to be frank about my experiences with React. I’ve been using it heavily for far over 6 or 7 years and I am doing great. React is amazing!
I know that people will always find ways to shoot themselves in the foot. At the same time I understand that batteries-not-included approach will lead to that sad result. When it comes to React projects I feel that I know a few tricks to sidestep problems that people discuss online.
Let's speak about the main problems first. People get out of their skin and try to make React projects a complicated and entangled mess. In programming, there was always a semi golden rule, to not architecturally fuse your business logic to any particular framework. We logically isolate our app from whatever framework we are using at the moment.
I’ve given the same demo day (task) exercise to ~50+ React devs. Basically two inputs and a button to draw a pattern on an HTML canvas. You wouldn’t believe how many of them (roughly 47) completely and unnecessary implemented most of the business logic inside React components. The sad part is this app doesn’t need React at all. Or one could implement 99% of the business logic in pure JS and just call one function from React.
React gives an option to store business logic state in
useState and manage it via
useEffect and we gladly use that to our demise. Litter our code with singleton instances of a business logic because we’ve “forgot” how to do a DI without a framework.
Second there is this immutable transducer reselect memoized concurrent proxy suspense state promotion society. Performance zealots obsessing over trillion renders per second. Which they don’t deliver, by the way. They describe how their Rube Goldberg flux redux machine works. A machine to read and write data into a JSON object.
React is pretty bare bones and before critiquing it we should take a hard look in the mirror. In my view, the framework is doing it’s job just fine. Unless we learn what really goes wrong with React projects we are doomed to repeat the same mistake again and again in the next (pun intended) framework.
People have asked me to point out a good repo. I would love to point to a good repo, but all the apps I’ve worked with are closed source.
I have a recipe for a React project that “work” and I've seen many that are a mess. I don't have a reference repo at the moment but I will do it one day. I will put a link on this page. You can follow me on twitter or GitHub for an update.
While I don't have any demo repo now, there is on approximation I can show. A CRA playground for a DI lib I am working on. You can see that all the business logic is located in stores/services and React only attaches to them. Not a perfect example, but might be an inspiration. You can see that business logic is completely detached from React. I can run this kitchen app in a Node/CLI mode. I can swap React for web components or svelte. UI is an afterthought
Principles that work for me
What I can share are principles that keep me and my friendly teams happy when working with 50K+ non trivial React projects. I will say some controversial stuff and I will make many people angry. Inb4 the rage comments, I've heard the argumentation before and I don't really want get into fights.
Having said that, I assume that I might be totally wrong and there are many better approaches, but I haven’t seen a better approach applied in a “real” project.
Don’t write React apps
First of all, writing React or Vue or Angular or Whatever apps is silly. Your should write JS apps. Maybe write some Node.js apps without any fancy framework to practice. Your application should work in any environment. You should be able to run it in CLI mode and it should work just fine. Then add a few APIs for UI to your app. As little as possible.
Write good JS apps
Try to add a bit of clean architecture, DRY, SOLID, TDD and other spices. Maybe read “The Grug Brained Developer” for an inspiration?
Avoid framework built in state tools
useReducer. There where meant for small stuff. Local state of hover or input text.
useEffect to capture local events like key press or window resize.
Don't store business logic in hooks
Just don't. Codebases like that where the hardest to debug. For no good reason.
Avoid shiny framework toys
All those Time Slicing Suspense stuff. Oh, don’t use this or that, or write lib this or that way because it it will not be compatible with suspense. You know the joke about the Suspense? You know why is it called Suspense? It takes 4 years to release it.
Old tools like Makefile are around for a reason in an age of rotating fads like suspense. They have passed the test of time. New stuff might work, probably not, no need to rewrite your app to adapt to the latest change of heart. And don’t get me started on “tearing”…
You are not Facebook and you are not solving Facebook level problems in your app. And by the way, those facebook apps are pretty buggy IMO.
Avoid the immutability cult
State, and endless React topic and place of well deserved mockery.
State management is is just writing to JSON with an
.onChange event. IMO Redux did set us back for a decade with those ideas, boilerplate and verbosity. If you don’t
createMemoizedReselectCreator, bro do you even redux? Btw check how easy it to make a slow redux by a dev from Wrike.
And the verbosity, just check recoiljs official docs. Adding or removing an item from a
todoListArray tokes two screens of make-it-immutable code. What happened to
todoListArray=null? Too simple? Why would you trade that one liner simplicity for 2 screens of boilerplate code?
State management simplicity beats any fancy time traveling which is not that useful anyway. We do debug complex backend apps without time travel, right? We have debuggers and stuff.
About state.. I personally suggest mobx. Didn’t really like MST, because I use TS and don’t want to describe types twice. How to organize mobx stores? Create an instance from composition root and just pass it around via a context.
note to myself: create a reference repo
Ignore the framework guys
Ignore people who develop frameworks. They develop a framework, but they don’t develop business apps like the rest of us. They don’t have a “dirty” experience of maintaining 100K LOC app which logic is based on
useEffect… have you seen apps with business logic scattered around 200 useEffect files? Pure madness
Onion Layer Architecture
Try to adhere to onion layer architecture principles. Fronted is inherently messy. To get something done you need many sources of information. It is best to layer them one on top of the other and avoid cross-talk.
Hydration issues are complex and complexity is our number one enemy.
At the same time SSR can save you from other complexities of SPA apps. Maybe we will not have to deal with fronted routing one day. It seems like modern island frameworks like Astro or "simple" servers like Remix can remove many complexities at the cost of adding few.
Backend is your friend as it can reduce complexity. Where possible, move logic to backend or backend for frontend.
BFF is pretty easy to maintain, test and utilize standard HTTP caching mechanisms. Reinventing cache with stuff like Relay is… not trivial.
Tactical advice: don’t write logic in React components
Don’t store logic in React components. Make them dumb. All of them.
Check out this post by Dan on on using
useEffect for a countdown timer. There are too many things to take care off. It is always better to sidestep complexity and smart solutions.
Resources worth checking
Are there any additional resources? For now, I don't have much to recommend:
I hope you have learned something.
If there is one thought you should take away from this post: build your React projects as if you have to rewrite it to a different framework in two weeks.
We can continue a conversation on Twitter or HN