При переходе на Material-UI v5, как работать с условными классами?

#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>
  );
}
 

Изменение замены нескольких классов стилей оформления в v5

Вот эквивалентный пример использования машинописи:

 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>
  );
}
 

Изменение замены нескольких классов стилей оформления в v5

Некоторые другие возможные подходы:

  • Используйте css поддержку эмоций и возможности эмоций для создания стилей
  • Используйте tss-react, чтобы сохранить синтаксис, аналогичный синтаксису makeStyles , но подкрепленный эмоциями (поэтому вы не включали бы в свой пакет как эмоции, так и JSS, как это было бы в случае, если бы вы использовали рычаги makeStyles @material-ui/styles )

Комментарии:

1. Спасибо, это сработало! Я добавлю еще одно замечание, которое может кому-то пригодиться: так как функция передается styled(Component)(() => { /*...*/ }) запускам во время рендеринга, мы можем вызывать хуки внутри. Таким образом, мы можем получить доступ к контекстному API в этой функции.

2. Отличный ответ! Интересно, есть ли еще пример машинописного текста. Я не могу понять, как печатать, чтобы разрешить передачу таких вещей, как open стилизованный компонент.

3. @JaanusVarus Я добавил пример машинописного текста

4. Это короче в машинописном виде: styled("div")<YourProps>(...) . Здесь нет необходимости заявлять React.ComponentType об этом.