DEV Community

Mike Botsko
Mike Botsko

Posted on

Reducing rerenders in nested/recursive components?

Codesandbox

I'm trying to decide how to port an old pure-js Tree library to React. Because tree components are recursive, no matter my approach I'm seeing rerenders (react devtools profiler w/"record why each component rendered" enabled) because state has changed and needs to be passed to children.

I've been able to avoid rerendering childless nodes by only passing state setters, but since state has to be passed to children, any node with children renders. So if I click a node to select it, all nodes with children rerender.

I'll need to eventually handle more than just an isSelected state, this is just a simple demo, but I can't figure out a good way to do this.

I could write my own logic to determine if a parent node needs to render but it would need know if any child nodes have changed state. A recursive check is bad, so I was thinking of setting some kind of isDirty flag on parent nodes, but being fairly new to react I don't know if there are better ways.

I'm hoping to avoid third party libraries as I'd like for this to be a public library eventually.

Top comments (2)

 
viveleroi profile image
Mike Botsko • Edited

I've been updating my local work with your recommended approach, I like it. How would you handle exposing the API methods in useTree to the application using this tree component? If they call useTree they'll just get a new instance. Whatever I do, I need to ensure it works for multiple tree instances. Maybe the ref is still the best approach?

Looking at the flamegraphs (ranked) I see that for some reason the render phase with this approach takes about 10ms but the actual commit happened at 0.8s. Imgur With my original approach, the render time was only ~3ms but the commit happened at 1.1s. Imgur

I realize that perf isn't the single dictating factor in how components are made but I'm curious why your approach takes longer to render yet less time to commit and mine takes less time to render but more time to commit. Again, at these millisecond times it's really not worth getting worked up over but I do need to be aware of perf as we'll have thousands if not more nodes.

EDIT: I tried this approach (expanding parent nodes, not selecting) with 10k nodes (100 nodes with 100 children each). It took 255ms to render and commit at 1.5s. That was a very noticeable lag in the UI. With my original setup, it took 55ms and commit at 0.9s which was better, far less noticeable. I will continue looking at my options, as I'm still learning. One is more "proper" but it's also slower.

Collapse
 
viveleroi profile image
Mike Botsko • Edited

Thanks, I appreciate your response and effort. I'm looking at your code now.

Can you clarify why it's better to move stuff upward? For example, my demo had the onclick in the TreeNode component but you pass it as a prop from the TreeNodes. Is there any technical advantage to that, or is better for management?

I've been using the react devtools profiler on these trees. I know that render times aren't the only factor, but I notice that when I select a node in my demo, the render takes around 2.2ms but around 5.1ms in yours. That's absolutely trivial but the app I might use this in could have 10k nodes so I'm trying to understand how everything impacts the perf.

EDIT: Reading that article, I do see the mention of "the children are updating the parent's state, so we are effectively going against the one-way data flow." That's a problem in my example.