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...
For further actions, you may consider blocking this person and/or reporting abuse
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
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.
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
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
anduseCallback
all over my codebase super ugly - if that's what the "experts" recommend (and they do) then it only confirms the reservations I have :)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
I completely misunderstood
useEffect
, for example: I often useuseEffect
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...
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.
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. 😉
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
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?
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.
Thanks for the detailed explanation.
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 ... :)
Same here, I was so worried for the whole article ;)
Yeah that one looked genuinely weird :)
Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍
Good explanation on each topic, keep it up! Thank you!
Great article, you got my follow, keep writing!
holy shit this is awesome
Btw, same wrote in the documentation :-)
Thank you!
This is awesome! Hopefully everyone will read this article! 👍
Thanks for reading! For more articles, follow me on Instagram or here on DEV.
Dennis Persson
Great article
I think part of these anti-patterns is resolved with ESLint plugin for React
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.
Can't stand all the boiler plate code to even start anything.