I’m having an issue with identifying bottlenecks in render performance while working on a JSON viewer. With few elements, it performs well, but at a certain point it becomes annoyingly slow.
Checking the profiler, it seems that elements are rendering fast enough, but I’ve noticed a few issues that I’m not sure how to pursue.
performance.now()as well as checking the render time in React DevTools, the figures seem okay. I could be interpreting it wrong.
React.memo()on stateless elements (particularly the key/value which is the most frequently rendered component), but it doesn’t seem to improve the performance noticeably. Admittedly, I’m not sure if I understand the reasoning enough behind memoizing React components to implement this usefully.
There are two functionalities which reproduce a slow response time with (not so big) JSON documents:
With the current implementation, both filtering and expanding all triggers a
display: none change on the child elements, and the behavior leads me to believe I’m doing something inefficiently to handle this use case.
The code is available here: https://codesandbox.io/s/react-json-view-4z348
With a production build here (not performing any better): https://csb-4z348.vercel.app/
To reproduce the issue, play around with the Expand All function (plus sign next to filter input) and some filter inputs.
Then, try loading a JSON feed with more elements (you can test on my GitHub API feed) and try filtering/expanding all. Notice the major performance hit.
What I’ve noticed
While I would appreciate a nudge in the right direction for this specific case, what I’m most curious about is how best to identify what is causing these performance issues.
I’ve looked into windowing the output, but it’s not my first choice, and I’m pretty sure I’m doing something wrong, rather than the cause being too many elements rendered.
I appreciate your time, and thank you in advance for any tips you could provide!
It seems I’ve answered my own question. The problem was a reconciliation issue due to using UUID as a key prop in my child components, which caused them to re-render every time the minimize state changed. From the docs:
Keys should be stable, predictable, and unique. Unstable keys (like
those produced by Math.random()) will cause many component instances
and DOM nodes to be unnecessarily recreated, which can cause
performance degradation and lost state in child components.
I’ll leave the steps here for anyone else who runs into this issue.
After (too long) digging around in performance profiler, I noticed that each time I minimized or expanded the elements, each child was being mounted again. After consulting Google with a more specific query, I found this blog post and realized that I was committing this flagrant performance error.
After fixing the key prop, interaction time got ~60% faster for minimize/expand all.
Finally, I memoized some other components related to the instant filter and finally it seems to be performing as well as I would like for the time being.
Thanks to anyone who took a look at this in the meantime, and I hope it’s helpful for anyone who might come across this.