Reputation: 1863
In the official migration guide, they give the following example of changing the code from JSS (makeStyles
) to the new styled
mode.
Before:
const useStyles = makeStyles((theme) => ({
background: theme.palette.primary.main,
}));
function Component() {
const classes = useStyles();
return <div className={classes.root} />
}
After:
const MyComponent = styled('div')(({ theme }) =>
({ background: theme.palette.primary.main }));
function App(props) {
return (
<ThemeProvider theme={theme}>
<MyComponent {...props} />
</ThemeProvider>
);
}
This is fine for a single class, but what to do when the code has conditional classes?
e.g.
<main className={classnames(content, open ? contentOpen : contentClosed)}>
{/* content goes here */}
</main>
Here, content
, contentOpen
, and contentClosed
are classes returned from useStyles
, but contentOpen
and contentClosed
are rendered conditionally based on the value of open
.
With the new styled
method, instead of generating class names we're generating components. Is there a way to elegantly replicate the rendering without resorting to content duplication in the ternary expression?
e.g. we don't want to do something like:
function App(props) {
return (
<ThemeProvider theme={theme}>
{open
? <MyOpenComponent {...props}>{/* content */}</MyOpenComponent>
: <MyClosedComponent {...props}>{/* content */}</MyClosedComponent>
</ThemeProvider>
);
}
Upvotes: 2
Views: 1951
Reputation: 81026
There are quite a few possible ways to deal with this. One approach using styled
is to leverage props to do dynamic styles rather than trying to use multiple classes.
Here's an example:
import React from "react";
import Button from "@mui/material/Button";
import { styled } from "@mui/material/styles";
const StyledDiv = styled("div")(({ open, theme }) => {
const color = open
? theme.palette.primary.contrastText
: theme.palette.secondary.contrastText;
return {
backgroundColor: open
? theme.palette.primary.main
: theme.palette.secondary.main,
color,
padding: theme.spacing(0, 1),
"& button": {
color
}
};
});
export default function App() {
const [open, setOpen] = React.useState(false);
return (
<StyledDiv open={open}>
<h1>{open ? "Open" : "Closed"}</h1>
<Button onClick={() => setOpen(!open)}>Toggle</Button>
</StyledDiv>
);
}
Here's an equivalent example using TypeScript:
import * as React from "react";
import Button from "@mui/material/Button";
import { styled } from "@mui/material/styles";
const StyledDiv: React.ComponentType<{ open: boolean }> = styled("div")(
({ open, theme }) => {
const color = open
? theme.palette.primary.contrastText
: theme.palette.secondary.contrastText;
return {
backgroundColor: open
? theme.palette.primary.main
: theme.palette.secondary.main,
color,
padding: theme.spacing(0, 1),
"& button": {
color
}
};
}
);
export default function App() {
const [open, setOpen] = React.useState(false);
return (
<StyledDiv open={open}>
<h1>{open ? "Open" : "Closed"}</h1>
<Button onClick={() => setOpen(!open)}>Toggle</Button>
</StyledDiv>
);
}
Some other possible approaches:
css
prop and Emotion's capabilities for composing stylesmakeStyles
but backed by Emotion (so you wouldn't be including both Emotion and JSS in your bundle as would be the case if you leverage makeStyles
from @material-ui/styles
). This is the route I took when migrating to v5 and as part of my migration I created a codemod for migrating JSS makeStyles
to tss-react's makeStyles
.Upvotes: 7