Reputation: 21436
I want to find a way to easily define convenience components in my app using Material-UI.
I copied an example from the Material-UI doco for Appbar and tried to introduce some React components to increase readability.
My question is about the contrast in boilerplate between the MenuShortcutBar
function and the MenuShortcutItem
class below.
MenuShortcutBar
is fairly concise; but as soon as the Material-UI styling comes into it, I end up needing a lot of boilerplate. I had to define a class, a property interface which needs to extend WithStyles
and a constructor.
So the question: Is there a concise way to create styled React components when using Material-UI? How can I simplify MenuShortcutItem
?
import * as React from "react";
import {ReactNode, SyntheticEvent} from "react";
import {
AppBar,
Hidden,
IconButton,
Menu,
MenuItem,
SvgIcon,
Toolbar,
Typography,
withStyles,
WithStyles
} from "@material-ui/core";
import {SvgIconProps} from "@material-ui/core/SvgIcon";
import {SendMailSvg} from "component/svg-icon/SendMailSvg";
export interface MuiAppBarProps extends WithStyles<typeof styles> {
}
class MuiAppBar extends React.Component<
MuiAppBarProps,
{ anchorEl?: HTMLElement, }
>{
constructor(props: MuiAppBarProps, context?: any){
super(props, context);
this.state = {anchorEl: undefined}
}
handleMenu = (event:SyntheticEvent<HTMLElement>) => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: undefined });
};
render(){
const { classes } = this.props;
return <div className={classes.root}>
<AppBar position="static" color={"default"}>
<Toolbar variant={"dense"}>
<IconButton className={classes.menuButton} color="inherit">
<MenuIcon/>
</IconButton>
<IconButton className={classes.menuButton} color="inherit">
<SendMailSvg width={"2em"}/>
</IconButton>
<MenuShortcutBar>
<MenuShortcutItem classes={this.props.classes}>
Keywords
</MenuShortcutItem>
<MenuShortcutItem classes={this.props.classes}>
Forwarded
</MenuShortcutItem>
<MenuShortcutItem classes={this.props.classes}>
Rejected
</MenuShortcutItem>
</MenuShortcutBar>
</Toolbar>
</AppBar>
</div>
}
}
...
function MenuShortcutBar(props:{children: ReactNode}){
return <Hidden smDown>
{/* Avoid shortcuts wrapping which causes AppBar to grow in height */}
<span style={{
display: "flex", flexWrap: "nowrap", overflow: "hidden"
}}>
{props.children}
</span>
</Hidden>
}
interface MenuShortcutItemProps extends WithStyles<typeof styles> {
children: React.ReactNode
}
class MenuShortcutItem extends React.Component<
MenuShortcutItemProps,
any
>{
constructor(props: MenuShortcutItemProps, context?: any){
super(props, context);
}
render(){
return <IconButton color="inherit"
className={this.props.classes.menuButton}
>
{this.props.children}
</IconButton>
}
}
const styles = {
root: {
flexGrow: 1,
},
grow: {
flexGrow: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
};
export default withStyles(styles)(MuiAppBar);
Upvotes: 3
Views: 487
Reputation: 21436
Found some info in the Material-UI Typescript guide that helped me have a better attempt.
Thanks to Martin Hochel for his Typescript pro tips post that put me on to using type intersection to mix WithStyles
into the Props definition (I was hung up on trying to use extends
).
A more concise function-style definition:
const MenuShortcutItem2 = withStyles(styles)((
props:{children:ReactNode} & WithStyles<typeof styles>
) => (
<IconButton className={props.classes.menuButton} color="inherit">
{props.children}
</IconButton>
));
And a fairly concise class-style component definition for when state is needed:
const ScreenContainer = withStyles(style)(class extends React.Component<
{children:ReactNode} & WithStyles<typeof style>
>{
state = {message: "wibble"} as {message?: string};
render(){
return <main className={this.props.classes.mainLayout}>
<Paper className={this.props.classes.paper}>
{this.props.children}
{this.state.message}
</Paper>
</main>
}
});
Some problems with above:
The duplication in repeating withStyles(styles)
and WithStyles<typeof styles>
is mildly annoying.
There's also some slight duplication there of the property names in the defintion of the state, but it's still better than the standard constructor arrangement (where you duplicate the property names anyway, but need at least three more lines of boilerplate).
Upvotes: 1