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

Can withStyles pass props to styles object? #8726

Closed
gndplayground opened this issue Oct 17, 2017 · 54 comments
Closed

Can withStyles pass props to styles object? #8726

gndplayground opened this issue Oct 17, 2017 · 54 comments
Assignees
Labels
duplicate This issue or pull request already exists

Comments

@gndplayground
Copy link
Contributor

gndplayground commented Oct 17, 2017

Currently, I developing a component that requires both props and themes object.

At first, it works great with theme object

const styles = theme => ({
  title: {
    ...theme.typography.headline,
    textAlign: 'center',
    padding: '8px 16px',
    margin: 0,
    color: theme.palette.common.white,
    backgroundColor: theme.palette.primary[500],
  },

withStyles(styles, { withTheme: true })(Component);
....

But I also need access to props in styles object.

I tried example but it not working.

{
 ....
display: (props) => props.display
}

I end up combine react-jss and withTheme to do that

import { withTheme } from 'material-ui/styles';
import injectSheet from 'react-jss';

function withStyles(styles, Component) {
  return withTheme()(injectSheet(styles)(Component));
}

export default withStyles;

....

const styles = {
  title: {
    display: (props) => props.display,
    textAlign: 'center',
    padding: '8px 16px',
    margin: 0,
    color: ({ theme }) => theme.palette.common.white,
    backgroundColor: ({ theme }) => theme.palette.primary[500],
  },

It works but I really miss

  title: {
    ...theme.typography.headline,
@oliviertassinari
Copy link
Member

We should probably be able to address the issue by making Material-UI using the same context key as react-jss: https://github.com/cssinjs/theming/blob/master/src/channel.js#L1.
Also, have a look at #7633

@oliviertassinari oliviertassinari changed the title [v1]Can withStyles with option {withTheme: true} pass props to styles object? Can withStyles pass props to styles object? Oct 17, 2017
@oliviertassinari oliviertassinari added the new feature New feature or request label Oct 17, 2017
@oliviertassinari oliviertassinari self-assigned this Oct 17, 2017
@oliviertassinari
Copy link
Member

I have a PR ready with a react-jss interoperability example. I will add that into the docs. cc @kof

@pelotom
Copy link
Member

pelotom commented Oct 17, 2017

@oliviertassinari does the resolution of this mean that it should be possible now to get access to the props within the styles definition? It's not clear to me how...

@oliviertassinari
Copy link
Member

@pelotom no, withStyles doesn't have access to the properties. But given how much people are asking for this feature. It's something I can prioritize, after the bug fixes. You can use the injectSheet HOC, but it's open the door to multiple issues: memory leak, hot reloading broken, no classes composition, no internal ref access, broken theme nesting handling. At least, it's some of the issues I have been facing in the past and motivated my rewrite. I think that step by step those issues will be addressed.

@nmchaves
Copy link
Contributor

@oliviertassinari It's very reassuring to hear that you'll consider prioritizing this! This would make it much easier to customize components. For example, I'd like to have a checkbox with a configurable size (i.e. width & height in pixels):

<CustomCheckbox size={16} />

If we could access props in styles, this would be very simple:

const styles = {
  root: {
    width: props => props.size,
    height: props => props.size
  }
}

or

const styles = props => ({
  root: {
    width: props.size,
    height: props.size
  }
})

and then:

const CustomCheckbox = ({size, classes}) => <Checkbox className={classes.root} />;

export default withStyles(styles)(CustomCheckbox);

For now, do you have any recommendations about how we should approach these types of use cases? Or do you have any estimates of when you might be able to add support for accessing props when using withStyles?

@oliviertassinari
Copy link
Member

@nmchaves You use case seems to fall perfectly for the inline-style approach, you can find a bit about it in the documentation. FAQ
https://github.com/callemall/material-ui/blob/75a30061e76eae93c711ec202a2c7e4238a4f19a/docs/src/pages/style/SvgIcons.js#L38-L44

@nmchaves
Copy link
Contributor

Thanks @oliviertassinari ! I was hoping I could accomplish this using withStyles, but inline-styles will work great. And the fact that you're recommending it here + in the docs makes me feel very confident with this decision. Thanks again!

@payner35
Copy link

it would be nice to be able to pass a prop (image src) to the style for a backgroundImage

@caub
Copy link
Contributor

caub commented Feb 6, 2018

I'd wrap withStyle

const withStylesProps = styles =>
  Component =>
    props => {
      console.log(props);
      const Comp = withStyles(styles(props))(Component);
      // return <div>lol</div>;
      return <Comp {...props} />;
    };

const styles = props => ({
  foo: {
    height: `${props.y || 50}px`,
  }
});

export default withStylesProps(styles)(
  props => (
    <div className={props.classes.foo} style={{ ...props.style, background: 'yellow' }}>
      <h1>Hello!</h1>
    </div>
  )
);

demo: https://codesandbox.io/s/k2y01rj3w7

(I'm surprised ^ works without any ThemeProvider and JssProvider set up https://codesandbox.io/s/q6v7krx6, ah it initializes it)

@oliviertassinari oliviertassinari added duplicate This issue or pull request already exists and removed new feature New feature or request labels Feb 6, 2018
@oliviertassinari
Copy link
Member

oliviertassinari commented Feb 6, 2018

@caub It's working, but you need to be cautious with this pattern. The injected CSS will grow with the number of instances of the component. It's a duplicate of #7633. I haven't dug into the topic. But I believe @kof version use some performance optimization.

@oliviertassinari
Copy link
Member

@caub Thanks for sharing!

@caub
Copy link
Contributor

caub commented Feb 13, 2018

@oliviertassinari there's this https://github.com/cssinjs/react-jss/blob/master/readme.md#dynamic-values in react-jss, I wonder why it couldn't be used in material-ui? Also I understand your point where you say the inline style prop is perfect for dynamic values, but it's nicer to have all styles definitions in the same places. There's also https://github.com/airbnb/react-with-styles that would handle className and style for more efficient dynamic styles

@SrikanthChebrolu
Copy link

I am facing the same issue can some one help me out
`import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Drawer from 'material-ui/Drawer';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import List from 'material-ui/List';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import Hidden from 'material-ui/Hidden';
import Divider from 'material-ui/Divider';
import MenuIcon from 'material-ui-icons/Menu';
import { mailFolderListItems, otherMailFolderListItems } from './tileData';

const drawerWidth = 240;

const styles = theme => ({
root: {
width: '100%',
height: 430,
marginTop: theme.spacing.unit * 3,
zIndex: 1,
overflow: 'hidden',
},
appFrame: {
position: 'relative',
display: 'flex',
width: '100%',
height: '100%',
},
appBar: {
position: 'absolute',
marginLeft: drawerWidth,
[theme.breakpoints.up('md')]: {
width: calc(100% - ${drawerWidth}px),
},
},
navIconHide: {
[theme.breakpoints.up('md')]: {
display: 'none',
},
},
drawerHeader: theme.mixins.toolbar,
drawerPaper: {
width: 250,
[theme.breakpoints.up('md')]: {
width: drawerWidth,
position: 'relative',
height: '100%',
},
},
content: {
backgroundColor: theme.palette.background.default,
width: '100%',
padding: theme.spacing.unit * 3,
height: 'calc(100% - 56px)',
marginTop: 56,
[theme.breakpoints.up('sm')]: {
height: 'calc(100% - 64px)',
marginTop: 64,
},
},
});

export class ResponsiveDrawer extends React.Component {
state = {
mobileOpen: false,
};

handleDrawerToggle = () => {
this.setState({ mobileOpen: !this.state.mobileOpen });
};

render() {
const { classes, theme } = this.props;

const drawer = (
  <div>
    <div className={classes.drawerHeader} />
    <Divider />
    <List>{mailFolderListItems}</List>
    <Divider />
    <List>{otherMailFolderListItems}</List>
  </div>
);

return (
  <div className={classes.root}>
    <div className={classes.appFrame}>
      <AppBar className={classes.appBar}>
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={this.handleDrawerToggle}
            className={classes.navIconHide}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="title" color="inherit" noWrap>
            Responsive drawer
          </Typography>
        </Toolbar>
      </AppBar>
      <Hidden mdUp>
        <Drawer
          variant="temporary"
          anchor={theme.direction === 'rtl' ? 'right' : 'left'}
          open={this.state.mobileOpen}
          classes={{
            paper: classes.drawerPaper,
          }}
          onClose={this.handleDrawerToggle}
          ModalProps={{
            keepMounted: true, // Better open performance on mobile.
          }}
        >
          {drawer}
        </Drawer>
      </Hidden>
      <Hidden smDown implementation="css">
        <Drawer
          variant="permanent"
          open
          classes={{
            paper: classes.drawerPaper,
          }}
        >
          {drawer}
        </Drawer>
      </Hidden>
      <main className={classes.content}>
        <Typography noWrap>{'You think water moves fast? You should see ice.'}</Typography>
      </main>
    </div>
  </div>
);

}
}

ResponsiveDrawer.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};

export default withStyles(styles)(ResponsiveDrawer);
`

screen shot 2018-02-13 at 3 27 41 am

@kof
Copy link
Contributor

kof commented Feb 13, 2018

The injected CSS will grow with the number of instances of the component.

@oliviertassinari injected CSS will grow +- same way html would grow with inline styles. Static styles are rendered in separate sheets and reused across all component instances.

@up209d
Copy link

up209d commented Mar 23, 2018

I did like this, though with stateless component it will re-render the withStyle from render to render, we can avoid by using full pure component.

import React from 'react';
import {
  withStyles,
  Grid,
  CircularProgress
} from 'material-ui';

const PreloadComponent = props => {
  const { classes,size } = props;
  return (
    <Grid className={classes.container} container justify={'center'} alignItems={'center'}>
      <CircularProgress size={size}/>
    </Grid>
  )
};

const StyleWithThemeProps = (props) => {
  return withStyles(theme => ({
    container: {
      paddingTop: props.size*2 || 50,
      paddingBottom: props.size*2 || 50,
    }
  }),{withTheme: true})(PreloadComponent)
};

const Preload = props => {
  const { size } = props;
  const WithStylesPreloadComponent = StyleWithThemeProps(props);
  return (
    <WithStylesPreloadComponent {...props}/>
  )
};

Preload.defaultProps = {
  size: 20
};

export default Preload;

We can use full pure component to avoid updating

const PreloadComponent = props => {
  const { classes,size } = props;
  return (
    <Grid className={classes.container} container justify={'center'} alignItems={'center'}>
      <CircularProgress size={size}/>
    </Grid>
  )
};

const StyleWithThemeProps = (props) => {
  return withStyles(theme => ({
    container: {
      paddingTop: props.size*2 || 50,
      paddingBottom: props.size*2 || 50,
    }
  }),{withTheme: true})(PreloadComponent)
};

class PreloadFull extends React.PureComponent {

  constructor(props,context) {
    super(props);
  }

  componentWillMount() {
    this.StyledPreloadFull = StyleWithThemeProps(this.props);
  }

  componentWillUpdate(nextProps) {
    this.StyledPreloadFull = StyleWithThemeProps(nextProps);
  }

  render() {
    const { StyledPreloadFull,props } = this;
    return (
      <StyledPreloadFull {...props}/>
    );
  }
}

PreloadFull.defaultProps = {
  size: 20
};

export default PreloadFull;

@caub
Copy link
Contributor

caub commented Mar 23, 2018

@up209d It works, but it's quite painful, I'll try to modify withStyles, to use more directly https://github.com/cssinjs/react-jss which can pass props in values

@SrikanthChebrolu could you move your message to a different issue, since it's not in-topic?

@chazsolo
Copy link

Just curious what the status is on this? I've been reading through this issue, the JSS docs, material-ui docs, and yet to find a solution for Mui+Jss+TypeScript that doesn't require me to use inline styles. Putting a few inline styles is sometimes unavoidable, but in my case there are multiple styles that have many different states, all relying on theme and props together 😞

@up209d
Copy link

up209d commented Jul 27, 2018

@chazsolo Hey Chaz, you actually can use injectSheet from react-jss instead of withStyles from mui. By that way you can have both props and theme.

import injectSheet from 'react-jss';

const styles = theme => ({
  container: {
     color: props => theme.palette[props.color || 'primary'].main
  }
});

...

export default injectSheet(styles)(AnyComponent);
import { JssProvider, jss, createGenerateClassName } from 'react-jss/lib';
import { MuiThemeProvider } from 'material-ui';

const generateClassName = createGenerateClassName();

...

<JssProvider jss={jss} generateClassName={generateClassName}>
  <MuiThemeProvider theme={props.theme} sheetsManager={new Map()}>
    <App/>
  </MuiThemeProvider>
</JssProvider>

@kof
Copy link
Contributor

kof commented Jul 27, 2018

@chazsolo I think you want to follow this issue cssinjs/jss#682

@chazsolo
Copy link

Thanks @kof and @up209d - subscribed and giving up209d's example a shot.

@chazsolo
Copy link

chazsolo commented Jul 27, 2018

@up209d
Unfortunately I don't think that's gonna work for me - I've implemented what you suggested, and I can see the props within the function call inside the styles object, but I continue to get errors. IAm I just missing types? I'm extending WithStyles in props Interfaces so I have access to the classes object in props (now I'm wondering if that's the problem referenced in #8726 (comment))

TS2344: Type '(theme: ITheme) => { arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcM...' does not satisfy the constraint 'string | Record<string, CSSProperties> | StyleRulesCallback<string>'.
  Type '(theme: ITheme) => { arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcM...' is not assignable to type 'StyleRulesCallback<string>'.
    Type '{ arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcMovement: { strokeDa...' is not assignable to type 'Record<string, CSSProperties>'.
      Property 'arc' is incompatible with index signature.
        Type '{ stroke: string; strokeWidth: (props: any) => string | number; }' is not assignable to type 'CSSProperties'.
          Types of property 'strokeWidth' are incompatible.
            Type '(props: any) => string | number' is not assignable to type 'string | number | undefined'.
              Type '(props: any) => string | number' is not assignable to type 'number'.

My theme looks like:

import { ITheme } from '...';

export default (theme: ITheme) => ({
  arc: {
    // ...
    strokeWidth: (props: any): number | string => {
      // this logs the correct data I'm expecting
      console.log(props.data[0].properties.name)
      return 1.5
    }
  },
  arcMovement: {
    // ...
  },
})

The interesting this is, when I use the classes object within my component, arc and arcMovement are valid properties:

// from Chrome console
{
  arc: "Arcs-arc-0-2-1 Arcs-arc-0-2-3",
  arcMovement: "Arcs-arcMovement-0-2-2"
}

Update

I was able to get this working, but as noted in the comment above, I had to strip out all references to WithStyles, withStyles, and I lose classes composition and theme nesting. I'm gonna give it a rest now and just keep an eye on the threads. Thanks for all the help!

@up209d
Copy link

up209d commented Jul 28, 2018

@chazsolo Hey Chaz, I am not sure but is that you want to access to classes inside the props of the style object. If so, I think it is impossible since the classes only available after jss processed the style object, how can you access classes before a process of making classes even hasn't been triggered?

@jgabriele
Copy link

jgabriele commented Nov 20, 2018

@iamthuypham your solution has the drawback of creating a new <style> tag each time a component gets created. Also, you might be careful when using defaultProps and add them to your HOC'd component and not the base components.

image

All of that is supported by react-jss, can't it be supported natively by material-ui?

Also, I think @jdolinski1 's problem is that your code does not propagate children the wrapped component may have.

@up209d
Copy link

up209d commented Nov 21, 2018

@iamthuypham I think it is not recommended to do that, as I used to do like that in the past, and you might experience the poor performance as long as the app growth very soon. Creating a new instance of component with new jss style object is not good in term of coding principle because the style object gotta be re-rendered entirely, again and again, every time per props change. Using injectSheet from react-jss is better choice. If you look into the injectSheet you will see that it break your style object into 2 pieces (static & dynamic) so only the dynamic get re-rendered when props change.

@koutsenko
Copy link

how to use plugins such as jss-nested with injectSheet?.

with injectSheet i can't get '&:hover' statements work.
with withStyles i can't access to props...

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 7, 2019

@koutsenko Here is an example:

import React from "react";
import { makeStyles } from "@material-ui/styles";
import Button from "@material-ui/core/Button";

const useStyles = makeStyles({
  root: {
    background: props => props.color,
    "&:hover": {
      background: props => props.hover
    },
    border: 0,
    borderRadius: 3,
    color: "white",
    height: 48,
    padding: "0 30px"
  }
});

export default function Hook() {
  const classes = useStyles({
    color: "red",
    hover: "blue"
  });
  return <Button className={classes.root}>Hook</Button>;
}

https://codesandbox.io/s/pw32vw2j3m

I hope it helps.


Wow, it's amazing the progress we have made in ~1 year 😍.

@stunaz
Copy link
Contributor

stunaz commented Jan 7, 2019

now how do you typescript that?

@oliviertassinari
Copy link
Member

@stunaz Good question. I don't know. I haven't looked into it. @eps1lon has done the TypeScript definition of the module. You can use it as a starting point.
https://github.com/mui-org/material-ui/blob/f4281a77d15b0d6eec9d33cdc358cfb89844996d/packages/material-ui-styles/src/index.d.ts#L72

@koutsenko
Copy link

@koutsenko Here is an example:

Thanks @oliviertassinari , with "react@next" it works now.

@up209d
Copy link

up209d commented Jan 8, 2019

@koutsenko If you couldn't make jss-nested work, it must be a configuration issue somewhere in your coding. As jss-nested is included in jss-default-preset, so it just works oob

https://stackblitz.com/edit/react-py6w2v

@Guardiannw
Copy link

Guardiannw commented Jan 11, 2019

@oliviertassinari

Can you also set the entire styles object for a given selector with props? To where you can conditionally apply a property?

For instance, like this

withStyles({
    root: {
        '& > path': (props) => {
            if(props.color)
                return {
                    fill: props.color
                };
           return {};
        }
    }
})

So that, if the prop does not exist, then it uses the previous fill value, rather than something else that I have to set it to? For instance, there are other rules that would normally apply to fill, but I only want to set this new fill property if the color prop is set.

Thanks!

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 11, 2019

@Guardiannw For some reason your variant doesn't work. Maybe @kof could raise our light on why 💡. You can do one of the following:

// 🏆
const useStyles = makeStyles({
  root: {
    "& > span": {
      backgroundColor: props => props.color || null,
    }
  }
});

// or 

const useStyles = makeStyles({
  root: props => ({
    "& > span": {
      backgroundColor: props.color || null
    }
  })
});

@Guardiannw
Copy link

@oliviertassinari I am having a hard time getting your second option to work with the withStyles function. Does it only work with makeStyles and hooks?

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 11, 2019

@Guardiannw It's working with any of the APIs of @material-ui/styles.

@kof
Copy link
Contributor

kof commented Jan 12, 2019

@oliviertassinari looks like a valid syntax, fn values were added in v10, so either v9 was used or I need a codesandbox reproduction

@Guardiannw
Copy link

Ok, that's what I tried it with. Might have to try again.

@contrerasjf0
Copy link

@oliviertassinari I have a question about the use of @materia-ui/styles, is It available and to use in a production environment?, in the documentation indicates that it doesn't work with the stable version, I'm using the "3.9.1", the example https://github.com/mui-org/material-ui/issues/8726#issuecomment-452047345 that you present it has a powerful and useful feature that I need. In these issues, I saw many comments from a different perspective and also I like the solution https://github.com/mui-org/material-ui/issues/8726#issuecomment-363546636 of @caub, but your comment about his solution is good.

@eps1lon
Copy link
Member

eps1lon commented Feb 8, 2019

@contrerasjf0 @material-ui/styles is only available as an alpha release. We treat alpha versions like most packages in the react ecosystem. I would recommend you never use any alpha packages in production. If you do you should expect bugs and breaking changes between any release i.e. you should be able to handle the churn alpha versions add.

What I hope is that people use those versions either in hobby projects or use it on a separate branch that is not deployed to production but still tested just like the production branch. I do appreciate everyone that uses those alpha versions and gives us feedback for them.

@koutsenko
Copy link

@up209d yes, your solution work, but with
styles = { name: { cssprop: props => {} } notation, not
styles = props => ({ name: { cssprop: {} })

Also, JssProvider isn't necessary.

@caub
Copy link
Contributor

caub commented Apr 8, 2019

@koutsenko

// at value level:
styles = { name: { cssprop: props => value }
styles = theme => ({ name: { cssprop: props => value })

// at class name level
styles = { name: props => ({ cssprop: value })  }
styles = theme => ({ name: props => ({ cssprop: value })  })

You can't access props at the top level, even as a second argument after theme

@andreasonny83
Copy link

andreasonny83 commented Apr 21, 2019

I found a way

// MyComponent.tsx
import React, { PureComponent } from 'react';
import { myComponentWithStyles } from './myComponentWithStyles';

export interface MyComponentProps {
  copy: string;
  size?: number;
}

export class Twemoji extends PureComponent<myComponentWithStyles> {
  public render() {
    const { copy, classes } = this.props;

    return (
      <div className={classes.message}>
        {copy}
        <img src="https://via.placeholder.com/150" />
    </div>
    );
  }
}

// myComponentWithStyles.tsx
import React from 'react';
import { withStyles, WithStyles, Theme } from '@material-ui/core';
import { MyComponent, MyComponentProps } from './my-component';

const styles = (props: Theme & MyComponentProps) => ({
  message: {
    fontSize: props.typography.caption.fontSize,
    'box-sizing': 'content-box',
    '& img': {
      width: `${props.size || 24}px`,
      height: `${props.size || 24}px`,
      padding: '0 4px',
      verticalAlign: 'middle',
    },
  },
});

export type myComponentWithStyles = WithStyles<any>;

export const Component = (props: MyComponentProps) => {
  const StyledComponent = withStyles((theme: Theme) => styles({ ...props, ...theme }))(
    MyComponent
  );

  return <StyledComponent {...props} />;
};

The exported component can now be used like this:

<Component size={12} />

@oliviertassinari
Copy link
Member

@andreasonny83 Avoid this pattern. We are providing a native API in v4.

@andreasonny83
Copy link

@oliviertassinari thanks for the update. Is that pattern already available? Any documentation available?

@oliviertassinari
Copy link
Member

https://next.material-ui.com/css-in-js/basics/#adapting-based-on-props

@andreasonny83
Copy link

One last question @oliviertassinari . Can I use makeStyles in combination with withStyles?

I cannot find documentation for that. What I'm trying to do is this:

const useStyles = makeStyles({
  message: {
    boxSizing: 'content-box'
  }
});

export const ComponentWithStyles = withStyles(useStyles())(MyComponent);

@caub
Copy link
Contributor

caub commented Apr 21, 2019

@andreasonny83

Use either one or the other, in your example just remove makeStyles:

const styles = { message: {boxSizing: 'content-box', background: props => props.bg} };
export const ComponentWithStyles = withStyles(styles)(MyComponent);

@ghost
Copy link

ghost commented Sep 5, 2019

Gday folks thought id share my current solution with reference to the above discussion, hopefully it helps someone or someone could offer better advice on my current solution. For my signin page id like a random background image but id still like to maintain the power of the material ui api. The AuthPage is just the parent presentation layer that takes the individual auth components (signin, locked, forgotten-password, password-reset, etc) as children. Can confirm with each page refresh a new background loads aswell as a nice strongly typed props within AuthPageContainer prop

// AuthPage.styles.tsx

import { Container } from "@material-ui/core";
import { ContainerProps } from "@material-ui/core/Container";
import { withStyles } from "@material-ui/core/styles";
import React from "react";

interface IAuthContainerProps extends ContainerProps {
  background: string;
}

export const AuthContainer = withStyles({
  root: props => ({
    alignItems: "center",
    backgroundImage: `url(${props.background})`,
    backgroundPosition: "50% 50%",
    backgroundRepeat: "no-repeat",
    backgroundSize: "cover",
    display: "flex",
    height: "100vh",
    justifyContent: "center",
    margin: 0,
    padding: 0,
    width: "100%"
  })
})((props: IAuthContainerProps) => <Container maxWidth={false} {...props} />);

// AuthPage.tsx

import React from "react";
import forest from "../../assets/backgrounds/forest.jpg";
import sky from "../../assets/backgrounds/sky.jpg";
import uluru from "../../assets/backgrounds/uluru.jpg";
import { AuthContainer } from "./AuthPage.styles";

const AuthPage = ({ children }) => {
  const generateBackground = () => {
    const backgrounds = [forest, sky, uluru];
    const index = Math.floor(Math.random() * backgrounds.length);
    return backgrounds[index];
  };

  return (
    <AuthContainer background={generateBackground()}>{children}</AuthContainer>
  );
};

export default AuthPage;

@mwskwong
Copy link
Contributor

mwskwong commented Sep 13, 2019

simply do something like this:

// styles.js
export default theme => ({
	root: props => ({
		// some styles
	}),
	...
});

//container.js
export default withStyles(styles)(MyComponent);

@luky1984
Copy link

what about passing also state?

@mwskwong
Copy link
Contributor

mwskwong commented Sep 13, 2019

@luky1984
You can't. Instead you may do:

// Component.js
<Button
    className={`
        ${classes.button} 
        ${this.state.isEnable
            ? classes.enable
            : classes.disable}
    `}
/>

Or use clsx https://www.npmjs.com/package/clsx instead

@iyunusov
Copy link

iyunusov commented Aug 5, 2020

@caub Your solution ruins the jss generated classname order.
Like written here: #8726 (comment)
I have tried to use your solution, but your HOC component (withStylesProps) solution delays the call of it's withStyles, because it's wrapped, so calling it with classNames doesn't override the css.
Like: https://codesandbox.io/s/hocs-8uhw1?file=/index.js

background should be #0000000 and color: blue

@ghost ghost mentioned this issue Feb 15, 2021
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests