-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Description
- I have searched the issues of this repository and believe that this is not a duplicate. I expected that I will find the response by googling / searching in issues, but I found nothing. Sorry if this is a duplicate.
Summary 💡
A common style of getting class names for className prop is the following pattern:
const useStyles = makeStyles(theme => {
root: {
padding: theme.spacing(1),
},
})
The Material UI app is wrapped in ThemeProvider context. A component uses useStyles as follows:
function Component() {
const classes = useStyles();
return <Box className={classes.root}> ... </Box>
}
Changing theme object (via setTheme) causes that all makeStyles callbacks run with new theme, and it generates new set of classes used by application. The components are rerendered so that they read new class names in classes and React will then propagate the changes to browser DOM.
It looks that useStyles hook causes component to render even if there is no real change in class content. Let's take the example makeStyles above. If I can change e.g. a primary color in a theme, new run of makeStyles will return the same as before: root: { padding: 8 }. A component using useStyles could stay the same, there is no need to create new class name and rerender the component.
Proposal: At least some trivial memoizing should be added to makeStyles, so that if theme is changed, but a class returned by makeStyles has the same value, the class should stay the same; if all classes from makeStyles result are the same as before, a component using useStyles should not rerender.
Examples 🌈
const useStyles = makeStyles(theme => {
root: {
padding: theme.spacing(1),
},
child: {
display: 'flex',
},
})
Changing e.g. primary color in theme causes new run makeStyles, but the values of root and child are the same as before (shallow compare on root and child). A component using these useStyles should not be rendered, the MUI internals should swallow this change of theme which has no impact on this component.
const useStyles = makeStyles(theme => {
root: {
padding: theme.palette.primary.main,
},
child: {
display: 'flex',
},
})
Changing primary color in theme make root differ from previous run. The child may stay the same. The root class has changed, a component render must be triggered.
Motivation 🔦
I developed a quite complicated API visualizer with lot of rows, boxes, values, icons etc.; the DOM is really non-trivial. I provide a dark/light switcher. A use makeStyles / useStyles in virtually every component. Changing the theme from light to dark or vice versa has direct impact on few components; almost all makeStyles use only theme.spacing which I don't touch. Switching the theme causes that virtually all component must be recomputed, it's really slow in dev mode, and noticeable in prod mode.
If there was even a trivial optimization in makeStyles / useStyles (shallow compare on class objects), then changing of theme could be really fast, now it's lagging as whole application must be rendered again.