4
votes

I am trying to avoid typing redundant code. In multiple components I have created the same css classes. I want to add them to the theme using createMuiTheme() and then only using the style directly from the theme without having to call "classes" props on the Component props.

I have tried creating a theme on the root Component like this:

const theme = createMuiTheme({
  palette:{
      primary: {
        main: '#47286E',
        light: '#D91677'
      },
      secondary: {
        main: '#9156D8' 
      },
  },

 fab: {
    position: "relative",
    top: 0,
    marginTop: 20px
    textTransform: 'none',
    width: 220,
    height: 50
  },

});

then I passed theme down to the other components using

<MuiThemeProvider theme={theme}>

I tried importing the fab button directly inside the component

<Fab variant="extended" className={this.props.theme.fab} size='small'>
    Change
</Fab>

however I don't seem to get any value when I try to get fab css class. I just don't want to create a whole new

const styles = theme => {
    blabbla
}

and import it in every component using the "classes" props if what I want is already on the theme being passed down to its child components.

3

3 Answers

2
votes

I don't think createMuiTheme is meant for that.

Alternatively you could just create a style object that you want to reuse

const styles = {
  fab: {
    position: "relative",
    top: 0,
    marginTop: 20px
    textTransform: 'none',
    width: 220,
    height: 50
  }
};

And just import and use it wherever you need to

import fabStyles from '../../somewhere-everyone-can-get-it.js';
import styles from './styles-for-this-component.js';

...

withStyles({...fabStyles, ...styles})(Component);

If you need the theme and styles.

withTheme(withStyles({...fabStyles, ...styles})(Component));

You can also use recompose to clean this up.

1
votes

className={this.props.theme.fab} doesn't work because this.props.theme.fab is an object, not a class name. When withStyles is used, it takes care of generating a unique class name (e.g. available via props.classes.fab) as well as injecting the CSS for that class into the head of the document. The fab object in your theme has not had this work done for it.

The code below shows a couple ways you can avoid redundant code. The first is a withFab function that encapsulates the CSS and the call to withStyles.

The second uses the fab object more directly and just passes it to the style property rather than the className property. You could get the styles from theme.fab (rather than a separate js file) similar to your initial approach, but there isn't necessarily a reason to put this in your theme unless you care about being able to override it via different MuiThemeProviders in your rendering hierarchy.

There are a couple downsides to the second (style property) approach:

  • If you use this on a lot of different elements, the CSS is redundantly specified in the DOM on each of those elements.
  • It won't support CSS that requires classes (e.g. using pseudo elements like :hover)

So I would recommend something more similar to the first option (UseWithFab/withFab) shown below.

withFab.js

import withStyles from "@material-ui/core/styles/withStyles";

const styles = {
  fab: {
    backgroundColor: "lightblue"
  }
};
export const fabStyles = {
  backgroundColor: "lightgreen"
};
const withFab = component => {
  return withStyles(styles)(component);
};
export default withFab;

index.js

import React from "react";
import ReactDOM from "react-dom";
import withFab, { fabStyles } from "./withFab";

const UseWithFab = withFab(props => {
  return <div className={props.classes.fab}>Using withFab</div>;
});
const UseFabStyles = props => {
  return <div style={fabStyles}>Using fabStyles</div>;
};
function App(props) {
  return (
    <>
      <UseWithFab />
      <UseFabStyles />
    </>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit 0xlk0vxmxn

0
votes

These are good answers. I'm taking notes :) When I first read the post, I suspect the intuition was around using the overrides key in the built-in MUI theme.

I often find the overrides key a more convenient way to implement a format used "more often" in the app.

It's a two-step process:

  1. Decide which MUI component you want to modify
  2. Set the className prop using a naming convention that works for you
import clsx from 'clsx'; // not required, but handy in the long-run
import Fab from '@material-ui/core/Fab';

const Component = () => {
  ...
  return <Fab className=clsx('MyCustomFab') />
}
// my theme prop for the MUI <ThemeProvider theme={myTheme} />

export default createMuiTheme({
  ...
  overrides: {
    MuiFab: {
      root: {
        '&.MyCustomFab': { // note: the "no-space" => MuiFab AND ...
          backgroundColor: "lightblue",
        }
      }
    }
  }
}