DEV Community

Cover image for React Anti-Patterns and Best Practices - Do's and Don'ts

React Anti-Patterns and Best Practices - Do's and Don'ts

Dennis Persson on February 05, 2023

React may seem to be one of the least opinionated frameworks in the Wild West Web. Despite that, there's a lot of mistakes you can do and even more...
Collapse
 
leob profile image
leob • Edited

Agreed, my impression of React (having used it, and having read numerous articles similar to this one) is that it requires an inordinate amount of discipline and low-level knowledge to build anything even moderately complex with React which is (a) going to perform (i.e. not doing a ton of re-renders) and (b) going to be maintainable ...

I said it before, this kind of low-level stuff should be taken care of by the framework

Thread Thread
 
perssondennis profile image
Dennis Persson

Same thoughts here. I do like React, but one shouldn't really have to think about optimizations for everything one does. That's what frameworks should do.

Thread Thread
 
adevinwild profile image
adil

Absolutely not agree with you, React is a great library to design web apps

I feel like you're talking about premature optimization, honestly I never use useCallback and I never count the number of re-renders...
I just develop my features, and then I think about optimizing them and imo, it's the best way to not struggle with React

Once you know more about the patterns, the do's and don'ts, your last concern should be optimization... but maybe I'm wrong.

Thread Thread
 
leob profile image
leob • Edited

Okay, but why then do people write THAT MUCH about THIS very topic (React component re-rendering and how to prevent it), here on dev.to (and elsewhere)?

I'd almost say it's counter-productive - it definitely gives off the impression that React is all about low-level "nerding" to manually optimize one's code for the least amount of re-renders, rather than being able to focus on "business value".

If, in many (most?) cases it doesn't really matter, then why does it seem the #1 React topic on dev.to ... it does give me second thoughts about the framework, and I'm probably not the only one.

P.S. I find the "solution" which consists of sprinkling useMemo and useCallback all over my codebase super ugly - if that's what the "experts" recommend (and they do) then it only confirms the reservations I have :)

Collapse
 
leob profile image
leob

Two big takeaways:

1) Do yourself a big favor by doing 'dynamic' stuff (or anything, really) OUTSIDE a (function) component, not INSIDE of it - because if you do it "inside" the component declaration, then chances are it will always cause the component to re-render (I remember being bitten by this myself, heavily even)

2) React "the library" needs eternal baby-sitting, by the developer, in order to optimize (minimize) the amount of re-rendering - I'd argue that most of the "deep expertise" of expert React developers consists of intimate knowledge of what causes re-renders, and ways to avoid that :-D

Collapse
 
pankajsanam profile image
Pankaj • Edited

This is a really good and detailed article, but it just reinforced my dislike for React. I don't want to have to worry about all these optimizations; I expect the framework or library to handle them. I truly hope that Vue and Svelte will gain more traction and eventually surpass React.

Unfortunately, I have to use React because of its market share, popularity, and the size of its community. If given the chance, I would switch away from this library in a heartbeat

Collapse
 
jankapunkt profile image
Jan Küster

I completely misunderstood useEffect, for example: I often use useEffect to listen for a specific prop and then reset the internal state and after that send back and "ok" to the parent.

Now React is really annoyed that I did not put all deps into the effect deps list, because that was my intention: run the hook only when this single prop changed... Now I have to work around all this with useCallback etc...

Collapse
 
perssondennis profile image
Dennis Persson

Me too thought about it that way before. The transition from class components to function components naturally lead to thinking about functional components in life cycles. And in the beginning, that might have been the intention, but today, that doesn't work very well.

useEffect is a bit flawed in its design. I would rather see it being replaced with other hooks which are easier to find perfect use cases for.

Collapse
 
leob profile image
leob

The first one had me confused:

Use useState Instead of Variables

until you said "move the variable declaration outside the component" and THEN it made sense ... :)

Collapse
 
bsides profile image
Rafael Pereira

Same here, I was so worried for the whole article ;)

Collapse
 
leob profile image
leob

Yeah that one looked genuinely weird :)

Collapse
 
raibtoffoletto profile image
Raí B. Toffoletto

Nice article! There are good tips here, some I have a different opinion on, but well done 🎉

If your reducer logic is good and prevents mutability you don't need a function to get its initial state. And you shouldn't use that variable anywhere else but to initiate state on mount... just my opinion. 😉

 
leob profile image
leob • Edited

Yeah well of course React is still dominant, very dominant, in the market (that is, the job market, and market share in general) - so, people's "low level" expertise regarding how to avoid all these performance pitfalls is worth gold - in other words, people who've "invested" in React obviously have a lot to lose ... :)

Riot.js may be great, but there's half a dozen other more or less new-ish frameworks (Svelte, SolidJS, I could go on) which are also great, each with a small or tiny market share - fragmentation which makes React stronger :)

Inertia ... the 900 pound gorilla effect of the "incumbent" ... the "it's the safe choice" effect ... the "nobody ever got fired for recommending IBM" effect ... and more classics like that :-D

Thread Thread
 
adevinwild profile image
adil

"why then do people write THAT MUCH about THIS very topic (React component re-rendering and how to prevent it)"

I guess people have discovered the value of certain hooks and really want to put them forward
But personally , it's absolutely not the thing I spend most of my time on when I create.

I used SolidJS, and really liked it, I even find the concept of signals much less limiting than states with React

On the other hand, I never tested Vue or Svelte and never heard of Riot.js 🤔

Collapse
 
truemail785 profile image
Ozair • Edited

Is it a good idea to leave the dependency array of useCallback empty so that the function is only initialized once? Because the callback will be generating output OR performing something based on the up-to-date state variables. Why do we reinitialize the function on the value changes if it is using the up-to-date state values directly. Or is it? Maybe we are re-initializing the function so that it doesn't use the old values. Is that the case here?

Collapse
 
perssondennis profile image
Dennis Persson

It is fine to leave the dependency array empty if you really don't have any dependencies it is dependent on. If it is dependent on state variables or functions, you should add all of them. If you don't do that, the function you have wrapped in useCallback will use an old instance of the dependency if the dependency reference updates.

Note that if you have a function as a dependency to a useCallback, that function would also need to be a useCallback function, because if that would be a normal function in a React component, the useCallback function would be recreated every render since the normal function's reference changes every time.

As I wrote in the article, you should always add all dependencies to all hooks with dependencies (useEffect, useCallback, useMemo). Normally it's completely safe to add all dependencies to useCallback and useMemo, it won't infer any bugs (unless you are doing strange things in your code, for example if it's very important for the useMemo to run once).

With useEffects it's more difficult. You should still add all dependencies to it. But as described in the article (tip number 7), it can be tricky to do. If you see a useEffect with a missing dependency in some old code. Don't just add it without ensuring that the functionality still works, because the code's behavior may change.

Sometimes it is better to don't touch old code that works. And if you need to touch it, consider to refactor it. I have written another article describing how to deal with code changes. I think it's a good reading to learn about why it is important to be thoughtful when altering old code.

Collapse
 
truemail785 profile image
Ozair

Thanks for the detailed explanation.

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍

Collapse
 
brense profile image
Rense Bakker

This is awesome! Hopefully everyone will read this article! 👍

Collapse
 
naucode profile image
Al - Naucode

Great article, you got my follow, keep writing!

Collapse
 
corners2wall profile image
Corners 2 Wall

holy shit this is awesome

Image description
Btw, same wrote in the documentation :-)

Collapse
 
bsides profile image
Rafael Pereira

Good explanation on each topic, keep it up! Thank you!

Collapse
 
thanhtanpm2000 profile image
ThanhTanPM2000

Thank you!

Collapse
 
perssondennis profile image
Dennis Persson
Collapse
 
vanyaxk profile image
Ivan

I think part of these anti-patterns is resolved with ESLint plugin for React

Collapse
 
st80ene profile image
Etiene Essenoh

Great article

Collapse
 
thethirdrace profile image
TheThirdRace

it's a good practice to turn initial states into getter functions that returns the initial state object. Or even better, use libraries like Immer which is used to avoid writing mutable code.

It's an even better practice to NEVER mutate at all.

Using Immer or any other crutch will simply condition you to make that mistake when your crutch is not available.

Never mutate, always use a new object or array.

P.S. I'm perfectly aware mutations are sometimes necessary for performance reasons, but the amount of times you'll actually need these is so rare you can't justify using it as a go to solution.

Collapse
 
eurob12 profile image
eurob12

Can't stand all the boiler plate code to even start anything.