简体   繁体   中英

reactjs&mui - how to properly share styles?

I would like to share styles between my whole webapp.

My Problem: I am using makeStyles currently like this:

component_A.js

const useStyles = makeStyles({
    specific_style: {
        padding: '24px',
        // etc.
    }
})

function foo() {

    const classes = useStyles();

    return (
        <div className={classes.specific_style}>This is a styled div</div>
    )
}

component_B.js

const useStyles = makeStyles({
    specific_style: {
        padding: '24px',
        // etc.
    }
})

function bar() {

    const classes = useStyles();

    return (
        <div className={classes.specific_style}>This is another styled div</div>
    )
}

Therefore, I am having a lot of duplicated code at the moment. I thought about putting the classes into a theme and access it like {theme.specific_style} , is this possible? I tried it, but without success so far.

Could someone share a working example with me and those that follows? :)

Possible solutions I thought about:

  • I know it's possible by using native CSS, but that doesn't feel right to me.
  • It might also be possible to simply put the style class into its own .js and import it then, but that doesn't feel right either since I have already access to the theme - and I guess this is where such styles should live.

Using theme with the makeStyles() is one way of sharing styles throughout your filesystem. The makeStyles() function can accept a function in addition to accepting an object:

const useStyles = makeStyles((theme) => {
    root: {
        minWidth: '100%',
        color: theme.textColor,
    }
}

In this way you can use higher level theme components within your styles objects. What you will need to do is create some theme file where you keep all your shared styles, and then import that object in the same way that you would import a component.

import { lightTheme } from './theme';

Other options include using a Theme provider: https://material-ui.com/styles/advanced/

Also you could pass the theme a prop.

TL;DR: Based on your discussion, you want to make the styles before passing them to the theme provider:

const useStyles = makeStyles({specific_style: {spacing: 8}, /*more */});
function UseTheme() {
  const classes = useStyles();
  return (<ThemeProvider theme={classes}><DeepChild /></ThemeProvider>);
}
function DeepChild() {
  const theme = useTheme();
  return <span className={theme.specific_style}>specific_style spacing</span>;
}

Otherwise, you are passing a raw object instead of a CSS class name. Try your styles in this pastebin .

NL;PR: Now, in terms of style sharing rationale, since the theme provider is internally a context provider, it will reduce component reusability .

There are four styling scopes:

  1. Global style override: You mentioned it as the common CSS file, it can be achieved with the prefix '@global' when making styles . I agree with you, not the way here. It is useful when a library or component does not expose its classnames or do it via direct CSS overrride.
const useStyles = makeStyles(
 {'@global': {'.aGlobalStyleClass': {spacing:8} /*more */}
);
function UseTheme() {
  const classes = useStyles();
  return (<div className="aGlobalStyleClass">);
}
  1. Global Style component override: when creating the theme . This will affect all instances of the MUI component.
//from MUI's page
const theme = createMuiTheme({
  components: {
    // Style sheet name ⚛️
    MuiButton: {
      styleOverrides: {
        // Name of the rule
        textPrimary: {
          // Some CSS
          color: 'white',
        },
      },
    },
  },
});
  1. Contextual Style component override: this is the one you have been discussing, where you decide whether to override the component initial style, however, the component needs to be a consumer of a theme provider. "Apply it sparingly", perhaps moving your useStyles() to a common parent of the components is a better solution. The main example uses it.

  2. Local Style component override: your common classname={classes.style} or style={}. As mentioned before, the place where useStyles() is called makes a huge difference. Reduce style duplication by moving the style creation up in the component hierarchy.

const useStyles = makeStyles({specific_style: {spacing: 8}, /*more */});
function Parent() {
  const classes = useStyles();
  return (<><Child1 classes={classes}/><Child2 classes={classes}/></>);
}
function Child1({classes}) {
  return <span className={classes.specific_style}>specific_style spacing</span>;
function Child2({classes}) {
  return <div className={classes.specific_style}>specific_style spacing</div>;
}

I assume you are torn between contextual and local style overrides. There is a need for both, yet I will make any duplicate style to go away by scaling up the styles along the composition hierarchy since a change in the theme provider will affect all its component subtree.

If you plan to create custom utility classes and use them anywhere, you can add them to the theme override and access in your components using the class names.

Add the overrides to mui theme

 let theme = createMuiTheme({
      overrides: {
        MuiAppBar: {
          root: {
            transform: 'translateZ(0)'
          }
        }
      },
      props: {
        MuiIconButton: {
          disableRipple: true
        }
      }
    });

    theme = responsiveFontSizes(theme);

    theme.overrides.MuiCssBaseline = {
      '@global': {
        '.testMe': {
          color: 'red'
        },
        '.container-std': {
          [theme.breakpoints.up('lg')]: {
            maxWidth: '1200px',
            marginLeft: 'auto',
            marginRight: 'auto'
          }
        },
        '.container-wide': {
          margin: theme.spacing(2, 2)
        }
      }
    };

Inject into your layout

import { theme } from './styles';
import Footer from '../footer/Footer';

export default function Layout(props) {
  const { children } = props;

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline />
      <Grid container direction="column">
        <Grid item style={{ height: '70px' }}>
          <Header />
        </Grid>
        <Grid item>
          <div>
            {children}
          </div>
        </Grid>
        <Grid item>
          <Footer />
        </Grid>
      </Grid>
    </ThemeProvider>
  );
}

Use them in your Components

export default function HomePage() {
  const classes = useStyles();

  return (
    <Box className={`${classes.hero} container-wide`}>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM