Pahlevi Fikri Auliya
Pahlevi Fikri Auliya

Reputation: 4517

How to wrap React component with little performance penalty?

My team uses React MaterialUI library. To provide consistent UI Pattern and make it easy for us to customise MaterialUI's component, we wrap each MaterialUI's component in our own component. For example:

const style = {} // our project custom style for ListItemText
const OurListItemText = ({primary, secondary, classes}: Props) => (
  <MuiListItemText
    primary={primary}
    secondary={secondary}
    className={classes.text}
  />
) // we only expose primary and secondary props of the original MuiListItemText.
// Team members are blocked from customising other MUIListItemText's props

export default withStyles(styles)(OurListItemText)

MuiListItemText is the original MaterialUI's component, while OurListItemText is our wrapper component. In our project, only OurListItemText is allowed to be used.

As the snippet above, OurListItemText does nothing but forward the props to MuiListItemText. However, this affects the performance quite a lot:

React Flame graph

ListItemText bar on the top is from OurListItemText while the one below is from MuiListItemText. If we use MuiListItemText directly, it could be ~50% faster (we have tried), which is noticeable when we have 100 ListItemText. Removing withStyles HOC improves a bit, but not significantly.

ListItemText is only one example, we have similar performance issue on other wrapped components. (2 Typography in the graph above is another pair of our-wrapper-component and MUI's-original-component)

How to improve the performance of those simple props-forwarding-components?

Upvotes: 16

Views: 1698

Answers (2)

Andrii Muzalevskyi
Andrii Muzalevskyi

Reputation: 3329

If you want raw performance - do not wrap ListItem, but write your replacement instead

List item source doesn't have any extra dependencies, so Ctrl-C, Ctrl-V will work

Then adjust for your needs, remove code you don't need, etc...

That will guarantee max possible performance


The negative side - support cost will grow.

Upvotes: 0

Roy Wang
Roy Wang

Reputation: 11260

wrapping a React component adds one extra level of full React lifecycle (i.e. the wrapper needs to be mounted). Is it possible to avoid this?

You can avoid the lifecycles by avoiding JSX and calling the functions directly instead.
Eg.

{Component({ data: 1, children: 'Hello' })}

instead of

<Component data={1}>Hello</Component>

This blog post claimed to achieve 45% speed improvement with their test case.

This syntax might not be as readable/understandable though.

Some quote from Dan Abramov regarding this issue:

We're looking at optimizations like this in the context of Prepack but there's nothing that's going to be immediately useable in the next couple of months. Within a year or two we might have something.

Note that unless you're creating thousands of elements, the performance difference won't be noticeable. Also unless your components are very flat and simple, the "pure win" from this optimization will likely be much less relevant in practice.

I wouldn't wait for Prepack to do the optimisation though since the timeline is uncertain and the resulting optimisation might not be identical to this.

As for the significance of the performance improvement, it'd depend on your project and the only way to be certain is to try out and see the improvement for yourself.

Upvotes: 6

Related Questions