It’s clear that writing clean, testable & decoupled code is way harder and you might look at your own code and be ashamed of how bad it is.
But before we talk about what you should do in order to improve your react apps. Let’s understand what’s wrong with React.
Disclaimer: I love the react community & its main contributors. Here are just my two cents on what’s wrong and what you could do to make your code cleaner.
Simplicity
For good or bad, I think the root cause of spaghetti code it’s that they decided to provide a simple library and let the user solve a million questions on their own, for instance:
What routing to use?
How to fetch data?
Where to store the state?
How to store the state?
…and so on
So eventually every React project has its own decisions, in contrast, there is a concept of Convention over configuration (popularized by Ruby on Rails) where the idea is to provide answers for a lot of those things out of the box.
In the end, this is a battle between being flexible or standard, but lately, I’ve seen it’s hurting the community, because it overwhelms the developers, not everyone can configure the tools in a safe and proper way which only brings more problems down the line.
Prematurity
This is a clear complaint with “hooks”; they introduced them and got an outstanding adoption but lacked documentation. Then soon enough they started to see their usage and how people overused it or didn’t use it correctly, so they identified many scenarios that could be corrected with proper documentation, Thanks for the new beta documentation (Check it out).
But that was a mistake they did since they deliver hooks and IMO they didn’t even have a clear idea of what was the correct way to use which eventually caused a lot of tech debt.
Reactivity
I’m the least capable person to talk about reactivity but keeping in sync the state & the DOM it’s a complex problem to solve, but it’s even harder to expose an abstraction to all developers that’s easy to use.
React with a bunch of hooks/effects make the code hard to read and fully understand the execution because one simple change can result in quite a few executions of the Component Function. So it’s easy to lose track of the flow because we are never in control of it (which sorta makes sense).
In contrast, there are other alternatives that have nailed it like Svelte where they abstract that code from the developer which results in more readable and clean code.
Performance
There is no doubt that React is fast, but the problem is that most of the frameworks focus a lot of effort & discussions around how performant their libraries/frameworks are, but seems like they are putting on the side how easy & scalable writing code with their tools is.
This point tights back to the complex abstractions and missing some of the conventions over configuration ideas.
There are also some gotchas with some patterns that are advocate like the following:
High-order components (HOC)
were so popular but they could shadow other properties.
In the following example, does “isLoading” it’s referring to withLoadingUsers or withLoadingFriends?
So you might think, nobody is going to do this, since it’s pretty obvious, but when you have separate files, and ship features in different sprints, it’s easy for a developer just add another HOC wrapper which might cause more issues.
Hooks
React components could become huge, so it’s a good practice to move a lot of that logic into their own hooks, but that just means you’re moving code to other files and eventually have highly couple hooks with your services because there is no such thing as dependency injection.
Enough talk about what’s wrong, if you haven’t read Clean code, go and read it, that’s the first step to writing code in any language/framework you use.
For React, I’m not going to answer any of the initial questions because you could be using React Context, redux, redux-saga, etc, or Apollo client, react-query, etc.
But in terms of writing cleaner react code, you can start by:
Read the beta documentation here
Create small components
Use single responsibility principles with hooks.
Use React Context instead of props drill down.
Minimize the use of useEffect/memos/callbacks until you really need them.
Decouple business logic from React.
Augment or create models around your objects. For instance:
Let’s say you get back a user object with firstName & lastName and you need to render the full name
The problem with the above snippet is that probably that’s not the only place you’d need to display the full name, so eventually, you end up repeating the same code, but what if there is no last name? or whatsoever? you’d have inconsistency because you might not be fixing or handling different cases the same way.
A preferred option would be to augment the objects with either new properties or a helper method
It’s common to work on code bases that are built wrong, but the main idea is to challenge what’s the convention and investigate ideas of how to make it cleaner and more readable. As a follow-up action item would be to explore different design approaches like DDD or patterns that could help you make better choices in the future.