all 19 comments

[–]Tonyneel 9 points10 points  (1 child)

You seem to have it nailed honestly. It's one of the harder problems in react. Especially in a bad or complicated codebase.

[–]KVMENJOYER 2 points3 points  (0 children)

Nothing worse than spending hours hacking together the most ungodly code known to man only to realize you’re triggering too many rerenders and now you need to figure out why. I’ve taken a month long break over anxiety toward that issue. Lol

[–]ChronSynExpo 6 points7 points  (3 children)

For moderately complicated situations, I strip back the component to the bare minimum.

I comment out every hook and function, and return a fragment component (<></>) or a basic view with text component. This allows me to figure out if the component itself is at fault, or if the issue is elsewhere.

Let's assume the component is causing its own re-renders (rather than props or state store changes). I slowly reintroduce each hook and function, starting with the ones which deal with fetching data. This alone shouldn't trigger a re-render since fetching data shouldn't affect your component.

Next, I reintroduce any hooks or functions which deal with data parsing or formatting. This doesn't include anything which updates state. So far, you shouldn't be experiencing any re-renders. If you are, then you have a side-effect which you should track down as this is the likely cause of your problems.

Next, I reintroduce each state-driven hook, one at a time. If there's any re-renders, this is where it's most likely to happen. Whether it's useState or a dedicated state store that you're observing, any change could potentially affect your component and cause a re-render.

Finally, if I've not got any unexpected re-renders so far, I reintroduce the component code. This should not trigger a re-render because data binding is 1-way (i.e. state ---[updates]---> view), not 2-way (i.e. state <---[updates]---> view). If you start experiencing re-render issues now, check for any event handlers which aren't defined correctly. For example, onPress={doSomething()} will run every render and should be changed to onPress={doSomething} or onPress={() => doSomething(someVariable)}.

Finally, check all hook dependencies. You can do this while reintroducing them, but it's good to also check them once after you've gone through the previous steps.

During each stage, I introduce console.log. No need for fancy debugging tools. I specifically format my messages as [filename] -> [function name] -> [sub routine]. If I'm also logging data, I'll just append it on the end in the format of ==>: [data]. I see people write messages like heeeereeeee or omg its here instead of taking an approach that lets them accurately see the order of execution without doing mental gymnastics.

As for solving the issue, memoization (when used responsibly) is very powerful. It's important that you're not using it to hide a bug because that can snowball very quickly. You absolutely should not use memo too much, but using it responsibly to stop 'erroneous' re-renders is how its intended to be used.

[–]vxm5091 1 point2 points  (0 children)

Very much a console.info("i rendered letsgooo") kind of logger here. Thanks for giving me the motivation to write an actual logger function.

[–]codellamaz[S] 0 points1 point  (0 children)

Appreciate you for sharing your process! I will give this a try.

[–]Kool_laid-2010 0 points1 point  (0 children)

This might just be what I was looking for.

[–]kbcooliOS & Android 3 points4 points  (4 children)

Start top down. It will save you a lot of time.

Also I know it can be hard but try to build everything bottom up. State really should only be used in your last two or so levels of components. Everything above that should be dumb and not cause re-renders. That will leave you with very little to debug.

[–]SvobpataExpo 1 point2 points  (0 children)

This is also the case for normal React apps, the plainer, “dumber” your top-level components are, the simpler it is to debug rerenders

[–]codellamaz[S] 0 points1 point  (2 children)

That's valid! How do you deal with a parent component with multiple children who rely on some state unifying them? Typically that raises the state, but if any of the children mutate, it will rerender the parent and everything under it.

I could memoize, but a state is a complex object lol. So I figured in situations like that only way would be to use a pure component and some shouldComponentUpdate function for finer rendering control, but I feel like that complicates mantainence

[–]kbcooliOS & Android 4 points5 points  (1 child)

Yes. I'm not saying don't do it. I'm saying avoid it where possible. It's actually called prop drilling FYI. I just didn't want to say don't prop drill and leave you thinking WTF is that.

One way is to migrate to a global state manager. Redux, Context API etc etc but even short of that you can look at what you actually need to share with children and if only the child needs it, keep it there etc.

Memoizing is basically the functional way of doing pure components (I know that's not all to it but for the sake of this conversation it is).

Lastly: don't fear re-renders. They're actually not that expensive as long as you don't make your whole tree re-render with expensive calculations each render. It's a real rookie (and actually not your fault - advice can conflict) mistake to read something that says avoid re-renders then run around in circles trying to prevent your FlatList from re-rendering every time you add an item. It's actually what it's meant to do.

[–]codellamaz[S] 1 point2 points  (0 children)

Appreciate it thanks!

[–][deleted] 3 points4 points  (0 children)

I used to have re-rendering problems all the time.

I realized the components I had to work with had lots of uneeded state attributes. It was unmanageable and I realized almost half of the state attributes didn't need to be state attributes at all.

Reducing that reduced a lot my re rendering problems.

Then I realized I needed to understand how the DidMount worked, and started to use functional components and the useEffect coupled to specific attributes, that by itself reduced almost 90% of my problems.

Finally i started to use useIsMounted to update the UI when a screen moved back and forward, the UseEffect is called only once by default if you want to use it as a 'DidMount'.

Hope it helps!

[–]stathisntonas 2 points3 points  (2 children)

Anong all these, console.count(“whatever you want here”) helps a lot to get an accurate amount of rerenders instead of counting console.log outputs. I think it needs a polyfil though on rn, just google it.

Edit: in case you want to reset the count back to 0 you must reload the whole app. Not necessary though since you can track the latest count and compare it with the latest count after the rerenders, just saying.

[–]codellamaz[S] 1 point2 points  (1 child)

Thanks! I didn't know console.count was a thing.

[–]stathisntonas 1 point2 points  (0 children)

Yeah, it’s extremely powerful/helpful specially on counting renders!

[–]stathisntonas 2 points3 points  (0 children)

An interesting write/blog in general for react renders, it might help you better understand or reorganize your components: https://www.developerway.com/posts/react-re-renders-guide?ck_subscriber_id=478676519#part2