React: When profiling, enable the "Record why each component rendered while profiling." option.
July 30, 2021•323 words
I have spent hours over the last two days trying to optimize the performance of a large Material UI list. It contains 10-100 rows and without fail would render slowly (~230 ms on my laptop running Firefox on an Intel i7-9750h) which especially became a problem when rendering selection checkboxes in each row. The setup of this table was fairly complicated, with several columns common regardless of the parent component, but it also needed the ability to add custom columns. I ended up implementing that using a render prop system like this:
<SuperTable data={data}>
{(object) => (
<>
<CustomCell>{/* checkbox */}</CustomCell>
{/* ... other cells */}
</>
)}
</SuperTable>
Well, I beat my head against a wall for hours trying to figure out why things were going slow. I was trying React.memo
on components all over the place, digging into the React Developer Tools waterfall graph of component renders, moving props and state around, and still rendering was slow. Eventually I gave up, until today when I noticed in the React Developer Tools options the following: "Record why each component rendered while profiling."
I turned it on, turned on profiling, rendered this table, and clicked a few checkboxes, then examined my render waterfall to find I had completely ignored the constantly-changing prop that actually keeps track of each checkbox's state. That means that every time I clicked a checkbox, the state variable that every checkbox relied on was changing, which caused a re-render of that cell, which caused a render of the entire row, which was slow. So I moved the state prop out of the checkbox and calculated its checked prop value in the <SuperTable>
component and it cut the render times down to ~110 ms. Could probably be faster, but already much better.
A pretty silly mistake on my part and hours wasted, but a wonderful relief to find the option that would have helped with so much debugging.