#reactjs #material-ui
Вопрос:
В официальном руководстве по миграции они приводят следующий пример изменения кода с JSS ( makeStyles
) на новый styled
режим.
До:
const useStyles = makeStyles((theme) => ({
background: theme.palette.primary.main,
}));
function Component() {
const classes = useStyles();
return <div className={classes.root} />
}
После:
const MyComponent = styled('div')(({ theme }) =>
({ background: theme.palette.primary.main }));
function App(props) {
return (
<ThemeProvider theme={theme}>
<MyComponent {...props} />
</ThemeProvider>
);
}
Это нормально для одного класса, но что делать, если в коде есть условные классы?
напр.
<main className={classnames(content, open ? contentOpen : contentClosed)}>
{/* content goes here */}
</main>
Здесь, content
, contentOpen
, и contentClosed
являются классами , возвращаемыми из useStyles
, но contentOpen
и contentClosed
отображаются условно на основе значения open
.
С помощью нового styled
метода вместо создания имен классов мы создаем компоненты. Есть ли способ элегантно воспроизвести визуализацию, не прибегая к дублированию содержимого в троичном выражении?
например, мы не хотим делать что-то вроде:
function App(props) {
return (
<ThemeProvider theme={theme}>
{open
? <MyOpenComponent {...props}>{/* content */}</MyOpenComponent>
: <MyClosedComponent {...props}>{/* content */}</MyClosedComponent>
</ThemeProvider>
);
}
Ответ №1:
Существует довольно много возможных способов справиться с этим. Один из используемых подходов styled
заключается в использовании реквизитов для создания динамических стилей, а не в попытке использовать несколько классов.
Вот пример:
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),
"amp; 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>
);
}
Вот эквивалентный пример использования машинописи:
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),
"amp; 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>
);
}
Некоторые другие возможные подходы:
- Используйте
css
поддержку эмоций и возможности эмоций для создания стилей - Используйте tss-react, чтобы сохранить синтаксис, аналогичный синтаксису
makeStyles
, но подкрепленный эмоциями (поэтому вы не включали бы в свой пакет как эмоции, так и JSS, как это было бы в случае, если бы вы использовали рычагиmakeStyles
@material-ui/styles
)
Комментарии:
1. Спасибо, это сработало! Я добавлю еще одно замечание, которое может кому-то пригодиться: так как функция передается
styled(Component)(() => { /*...*/ })
запускам во время рендеринга, мы можем вызывать хуки внутри. Таким образом, мы можем получить доступ к контекстному API в этой функции.2. Отличный ответ! Интересно, есть ли еще пример машинописного текста. Я не могу понять, как печатать, чтобы разрешить передачу таких вещей, как
open
стилизованный компонент.3. @JaanusVarus Я добавил пример машинописного текста
4. Это короче в машинописном виде:
styled("div")<YourProps>(...)
. Здесь нет необходимости заявлятьReact.ComponentType
об этом.