chipimix
chipimix

Reputation: 290

material v5 migration: migrate from JSS to Emotion

I'm trying to migrate my material-ui project from JSS to Emotion in order to support react18. I ran the

npx @mui/codemod v5.0.0/jss-to-styled <path>

command successfully. However, when I try to run my app, I have the following error on all child components but not on the root parent component where theme is defined:

Uncaught ReferenceError: theme is not defined

I can pass theme as a prop to the child components, however the part of the code where i need theme is outside the scope of the component. So, this doesn't fix the issue. Additionally, this error doesn't occur on the parent root component because that's where theme variable is defined:

declare module '@mui/styles/defaultTheme' {
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  interface DefaultTheme extends Theme {}
}
const theme = createTheme(adaptV4Theme({}));

Thus, if I add the code above in all components, I don't have this error anymore and it fixes the issue. Having said that, I think this is not the correct approach.

Does anyone have any better suggestion? Thank you!

[EDIT] Here's what my code looks like:

export default function App() {
  return (
    <Router>
      <ThemeProvider theme={theme}>
        <Routes>
          <Route path="/" element={<Home />} />
        </Routes>
      </ThemeProvider>
    </Router>
  );
}

This works for the Home react function component, however it doesn't work for its children components :(

[EDIT 2] Here's an example of a child component:

import React, { useState, useEffect } from 'react';
import { styled } from '@mui/material/styles';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import TextField from '@mui/material/TextField';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import Link from '@mui/material/Link';
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';

const PREFIX = 'LogIn';

const classes = {
  paper: `${PREFIX}-paper`,
  background: `${PREFIX}-background`,
  avatar: `${PREFIX}-avatar`,
  form: `${PREFIX}-form`,
  submit: `${PREFIX}-submit`
};

const StyledContainer = styled(Container)((
  {
    theme
  }
) => ({
  [`& .${classes.paper}`]: {
    marginTop: theme.spacing(8),
    padding: theme.spacing(4),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    backgroundColor: theme.palette.background.paper,
  },

  [`&.${classes.background}`]: {
    display: 'flex',
    alignItems: 'center',
    background:
      'linear-gradient(200.96deg, #ffd194 -29.09%, #4DBD90 51.77%, #062627 129.35%) no-repeat center center fixed',
    position: 'absolute',
    top: '0px',
    right: '0px',
    bottom: '0px',
    left: '0px',
    margin: '0',
    height: '100vh',
    minWidth: '100vw',
    zIndex: '2',
  },

  [`& .${classes.avatar}`]: {
    margin: theme.spacing(1),
    backgroundColor: '#151d29',
    width: 56,
    height: 56,
  },

  [`& .${classes.form}`]: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },

  [`& .${classes.submit}`]: {
    margin: theme.spacing(3, 0, 2),
  }
}));
export const LogIn: React.FC<{ onLogin: Function, onSignup: Function }> = ({ onLogin, onSignup }) => {
...
return (
    <StyledContainer className={classes.background}>
      <Container component="main" maxWidth="xs" sx={{ zIndex: 3 }}>
        <CssBaseline />
        <div className={classes.paper}>
        ...
    );
}
export default LogIn;

Upvotes: 0

Views: 925

Answers (2)

chipimix
chipimix

Reputation: 290

I found the issue! I added theme as an argument to the arrow function and it is working - which makes sense. How it was:

const Root = styled('div')(() => ({
  [`& .${classes.chartParent}`]: {
    height: '160px',
  },
  // ... 
}));

Fixed code:

const Root = styled('div')(({theme}) => ({
  [`& .${classes.chartParent}`]: {
    height: '160px',
  },
  // ... 
}));

Upvotes: 0

Akis
Akis

Reputation: 2252

From the explanation of the problem you described I believe if you wrap your application in the ThemeProvider you should be able to do it only in the parent App component and all you children will have access to the theme.

For example

import { ThemeProvider } from '@mui/material/styles';
import { theme } from './Theme'; // This is where I have my theme, change with yours please  

function App() {
  return (
    <Router>
        <ThemeProvider theme={theme}> // Here is where I wrap all my pages and the children components can have access to the theme
            <Routes>
              <Route path={HOME_PAGE} element={<Home />} />
              <Route path={ABOUT_PAGE} element={<About />} />
            </Routes>
        </ThemeProvider>
    </Router>
  );
}

Here is my Theme file.

import { createTheme } from '@mui/material/styles';

export const theme = createTheme({
  palette: {
    primary: {
      main: '#2d9cdb',
      dark: '#2e85d4',
    },
    secondary: {
      main: '#2e85d4',
    },
    text: {
      primary: '#000000',
      secondary: '#2d9cdb',
    },
    ...
        },
  components: {
    MuiAlert: {
      styleOverrides: {
        standardSuccess: {
          backgroundColor: '#55ab68',
          color: '#FFFFFF',
          fontWeight: 'bold',
        },
        standardError: {
          backgroundColor: '#ed4a3a',
          color: '#FFFFFF',
          fontWeight: 'bold',
        },
      }
    }
  }
});

Then for example inside my home page i have an Alert component that uses the theme.

  <Alert severity="error" icon={false}>
    <Typography variant="h4" color="secondary" align="center" >
      Test text
    </Typography>
  </Alert>

Please let me know if it helped.

Upvotes: 2

Related Questions