Network Requests
It seems like everywhere you look in various dev communities people will say “of course you should use SWR, react-query, apollo, etc”
Yet, almost every project I’ve come onto has varying degrees of doing vanilla requests, where they just repeat themselves inside of a useEffect hook.
Need to track loading state? Yet another useState and more logic in this duplicated useEffect you find across the codebase.
The thing is, these smart query libraries do a lot more for you. If you want to rebuild them, then go for it. The key thing all of those libraries offer, is a query cache that de-duplicates requests and shares the data between many hooks.
Most of the roll-your-own useEffects I see, have no way to do the same request across components, without passing down props or making a higher order context every single time.
SWR and all the others, have this built in, making them way more useful, not to mention a lot of other features like automatic refreshes, loading states, and a lot more.
Duplicated Form Logic
Many apps start out with html forms, and then either roll their own shared form logic, or use some convoluted form library that makes things a mess to come into later.
Often times these forms become mega files and the form library they used does a small part while the file itself has a ton of extra validation logic and other things.
In React, the best type of form library is react-hook-form. I say it’s the best because this type of syntax is the simplest it can get, feel free to prove me wrong though
In addition, these “simple” form files end up with a ridiculous amount of useStates.
This isn’t even needed, as html forms support a lot of this stuff out of the box.
That’s why the ultimate approach in my opinion, is using Remix.
Take a look at how forms work in that framework.
The key here is vanilla forms, and a small layer on top providing better interactions.
Users don’t even need JS enabled on their site to use these forms. Which leads to my next point.
Inaccessibility (and Janky-ness)
Every React project I’ve worked on has parts like this, and they frustrate me to no end. I try to add backlog tasks to correct it when I see it…
Constantly, there will be some new feature added, where “pages” change, yet the URL doesn’t. I can’t stand when you go to a new area of content, reload the page, and you are somewhere else.
Page flashes.
When you first load something and you notice it shift, or switch to something else completely.
Weird loading states.
When you have cascading network calls and bad component hierarchy, this can happen. Click a button and stuff flashes to load for a split second then content drops back in.
Every one of these things can be solved by using a SSR framework for React.
I recommend Remix because their patterns enforce this a bit more. Even junior developers will be more likely to not make these mistakes.
For example, when submitting a form, it can use the browser’s loading state, which has existed for 30 years… and then change the content after. No need for a client side shift for fast form actions.
Of course, there will always be things the framework can’t save you from, like creating a tab layout without using anchor tags…
All of these are reasons people bash on js apps, yet none of this has to be the case, it is all fixable.
Complicated useEffects
No matter how many times Dan Abramov tried to make a guide for this hook, people still don’t read it. It’s a shame really.
Every place I’ve worked at, there will be a useEffect that is triggered based on some user action.
This could be triggered in the onClick or onChange, or whatever else is being clicked on / filled out at that point in time. But a dev would rather update their state and run an effect after it changes.
Overcomplicated Global State
Some apps will have mega contexts, global to the entire app… every time this happens I shake my head…
Can you guess why? I think the reason is something most devs do not think about.
Page splitting.
By putting a bunch of stuff into a global context, chances are it’s included even when it isn’t needed.
Most frameworks today have automatic route splitting, Next, Remix, Sveltekit, etc.
So when you start making a global modals file, and import every modal the app uses, and you make a shared data context with 6 things you usually need, you are boxing yourself in to a larger and larger minimum bundle size.
You want to make a new about page for your SaaS product? Now it has all that unnecessary code. Chances are an import of an import of an import includes some large library that is quite large, so you bundle is huge at this point, even if you just return a blank page with nothing.
In addition, many apps will do this AND actually include a smart data fetching layer like react-query. You can store anything in these fetching systems. If you have some global data that isn’t coming from an api, you can still use these to power it.
TLDR; stop using the root _app.tsx file in NextJS and stop putting more things in it. Just import what you need on each page so you don’t shoot yourself in the foot. Your SEO scores will thank you.
Mega Components
There’s a file I saw recently, had over 10 useEffects in a single file. At this point, the amount of state possibilities in one area are impossible for any human to contextualize in their brain.
The amount of re-renders and possible UI glitches coming from this many state changes will lead to a janky app.
Not to mention, how could you ever fully test all these code paths?
A much better solution is to logically split these massive files into single focused components doing 1-2 things max. Anyone can jump in and fix or add onto existing code, because we just dropped the mental capacity needed by 10x or more to comprehend everything going on.
These building block components are much faster to write tests for, compared to the mega file that nobody will ever write tests against because it’s too overwhelming and not worth it.
That’s it… for now
Hope you enjoyed this list. If you have been coding in React for a long time, I would love to hear what other problems you see often that I missed. As always, please consider leaving a comment or subscribing for free for more coding related content!