Skip to content
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

Merged
merged 2 commits into from
Nov 29, 2024

Conversation

siriwatknp
Copy link
Member

@siriwatknp siriwatknp commented Nov 28, 2024

closes #44555

Context

ThemeProvider recompose the theme and pass it to React context. The current implementation is not optimized, so if the parent of ThemeProvider rerenders, the theme send to React context is a new object causing the component that get the theme through useTheme to rerender (or run effect) again.


@siriwatknp siriwatknp added performance package: material-ui Specific to @mui/material customization: theme Centered around the theming features labels Nov 28, 2024
@mui-bot
Copy link

mui-bot commented Nov 28, 2024

Netlify deploy preview

https://deploy-preview-44588--material-ui.netlify.app/

Bundle size report

Details of bundle changes (Toolpad)
Details of bundle changes

Generated by 🚫 dangerJS against 3d5ff09

Comment on lines 412 to 437
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
});
Copy link
Member

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');
});

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@siriwatknp siriwatknp merged commit d0a5989 into mui:master Nov 29, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customization: theme Centered around the theming features package: material-ui Specific to @mui/material performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

useTheme returns a new reference every time if any provided theme has cssVariables: true
3 participants