-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ThemeProvider] Optimize theme
changes when enabling CSS theme variables
#44588
Conversation
Netlify deploy previewhttps://deploy-preview-44588--material-ui.netlify.app/ Bundle size reportDetails of bundle changes (Toolpad) |
it('theme should remain the same when ThemeProvider rerenders', () => { | ||
const theme = createTheme({ cssVariables: true }); | ||
|
||
function Inner() { | ||
const upperTheme = useTheme(); | ||
const [count, setCount] = React.useState(0); | ||
React.useEffect(() => { | ||
setCount((prev) => prev + 1); | ||
}, [upperTheme]); | ||
return <span data-testid="inner">{count}</span>; | ||
} | ||
function App() { | ||
const [count, setCount] = React.useState(0); | ||
return ( | ||
<ThemeProvider theme={theme}> | ||
<button onClick={() => setCount(count + 1)}>rerender</button> | ||
<Inner /> | ||
</ThemeProvider> | ||
); | ||
} | ||
render(<App />); | ||
|
||
fireEvent.click(screen.getByRole('button')); | ||
|
||
expect(screen.getByTestId('inner')).to.have.text('2'); // due to double rendering | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should theme
and upperTheme
be referentially equal? I was about to suggest that we could use another approach for the test: instead of counting renders, check that theme === upperTheme
, but I found that they're not referentially equal. Not sure if it's possible to do, just wondering if that's something we should aim for.
it('theme should remain the same when ThemeProvider rerenders', () => {
const theme = createTheme({ cssVariables: true });
function Inner() {
const upperTheme = useTheme();
return <span data-testid="inner">{(theme === upperTheme).toString()}</span>;
}
function App() {
return (
<ThemeProvider theme={theme}>
<Inner />
</ThemeProvider>
);
}
render(<App />);
expect(screen.getByTestId('inner')).to.have.text('true');
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point but const theme = createTheme({ cssVariables: true });
is not equal to const upperTheme = useTheme();
because it's recalculated within ThemeProvider
.
I could write a different test using React.useRef
to compare the previous and the incoming theme so that it's not rely on count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
closes #44555
Context
ThemeProvider
recompose the theme and pass it to React context. The current implementation is not optimized, so if the parent ofThemeProvider
rerenders, the theme send to React context is a new object causing the component that get the theme throughuseTheme
to rerender (or run effect) again.