在 Material-UI 中使用条件样式与样式 vs JSS
Using conditional styles in Material-UI with styled vs JSS
我正在使用 Material-UI v5 并尝试迁移到使用 styled
而不是 makeStyles
因为这似乎是“首选”方法现在。我知道使用 makeStyles
仍然有效,但我正在尝试采用新的样式解决方案。
我有一个代表导航链接的列表项列表,我想突出显示当前选中的那个。下面是我如何使用 makeStyles
:
interface ListItemLinkProps {
label: string;
to: string;
}
const useStyles = makeStyles<Theme>(theme => ({
selected: {
color: () => theme.palette.primary.main,
},
}));
const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
const styles = useStyles();
const match = useRouteMatch(to);
const className = clsx({ [styles.selected]: !!match });
return (
<ListItem button component={Link} to={to} className={className}>
<ListItemIcon>{children}</ListItemIcon>
<ListItemText primary={label} />
</ListItem>
);
};
(注意这里我使用 clsx 来确定 selected
样式是否应该应用于 ListItem
元素。)
如何使用 styled
实现此目的?这是我到目前为止想出的(注意:ListItemLinkProps
的界面没有改变,所以我没有在这里重复):
const LinkItem = styled(ListItem, {
shouldForwardProp: (propName: PropertyKey) => propName !== 'isSelected'
})<ListItemProps & LinkProps & { isSelected: boolean }>(({ theme, isSelected }) => ({
...(isSelected && { color: theme.palette.primary.main }),
}));
const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
const match = useRouteMatch(to);
return (
// @ts-ignore
<LinkItem button component={Link} to={to} isSelected={!!match}>
<ListItemIcon>{children}</ListItemIcon>
<ListItemText primary={label} />
</LinkItem>
);
};
那么,关于这个的两个问题:
这是执行条件样式的最佳方法吗?
另一个问题是我无法计算出 styled
声明的正确类型 - 我必须将 // @ts-ignore
注释放在 [=24= 上方] 因为其类型的声明方式。
Material-UI v5 使用 Emotion 作为默认样式引擎,并在内部始终使用 styled
以使想要使用 styled-components 而不是的人更容易不必将两者都包含在捆绑包中的情感。
虽然 styled
API 适用于很多用例,但它似乎不太适合这个特定用例。有两个主要选项可以提供更好的 DX。
一个选择是使用新的 sx prop available on all Material-UI components (and the Box component can be used to wrap non-MUI components to access the sx
features). Below is a modification of one of the List demos 演示这种方法(自定义 ListItemButton
模拟您的 ListItemLink
的角色):
import * as React from "react";
import Box from "@material-ui/core/Box";
import List from "@material-ui/core/List";
import MuiListItemButton, {
ListItemButtonProps
} from "@material-ui/core/ListItemButton";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import InboxIcon from "@material-ui/icons/Inbox";
import DraftsIcon from "@material-ui/icons/Drafts";
const ListItemButton = ({
selected = false,
...other
}: ListItemButtonProps) => {
const match = selected;
return (
<MuiListItemButton
{...other}
sx={{ color: match ? "primary.main" : undefined }}
/>
);
};
export default function SelectedListItem() {
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
index: number
) => {
setSelectedIndex(index);
};
return (
<Box sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}>
<List component="nav" aria-label="main mailbox folders">
<ListItemButton
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItemButton>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItemButton
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItemButton>
</List>
</Box>
);
}
这种方法的唯一缺点是它目前 notably slower 比使用 styled
,但它仍然足够快,适合大多数用例。
另一种选择是通过 css prop 直接使用 Emotion。这允许类似的 DX(虽然使用主题不是很方便),但没有任何性能损失。
/** @jsxImportSource @emotion/react */
import * as React from "react";
import Box from "@material-ui/core/Box";
import List from "@material-ui/core/List";
import MuiListItemButton, {
ListItemButtonProps
} from "@material-ui/core/ListItemButton";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import InboxIcon from "@material-ui/icons/Inbox";
import DraftsIcon from "@material-ui/icons/Drafts";
import { css } from "@emotion/react";
import { useTheme } from "@material-ui/core/styles";
const ListItemButton = ({
selected = false,
...other
}: ListItemButtonProps) => {
const match = selected;
const theme = useTheme();
return (
<MuiListItemButton
{...other}
css={css({ color: match ? theme.palette.primary.main : undefined })}
/>
);
};
export default function SelectedListItem() {
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
index: number
) => {
setSelectedIndex(index);
};
return (
<Box sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}>
<List component="nav" aria-label="main mailbox folders">
<ListItemButton
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItemButton>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItemButton
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItemButton>
</List>
</Box>
);
}
在我开发的应用程序中(我还没有开始迁移到 v5),我希望结合使用 styled
和 Emotion 的 css
function/prop。在它的性能稍微提高之前(我认为最终会发生),我很犹豫是否大量使用 sx
道具。尽管它在许多情况下执行得“足够快”,但当我有两个可用的类似 DX 选项并且其中一个的速度是另一个的两倍时,我发现很难选择较慢的那个。我选择 sx
道具的主要情况是我想为不同的断点或类似区域设置不同的 CSS 属性的组件,其中 sx
道具提供比其他道具更好的 DX选项。
相关回答:
这似乎对我使用 sx 有用
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
...
const isMobile = useMediaQuery(useTheme().breakpoints.down('md'));
...
<Divider sx={{ whiteSpace: isMobile ? 'normal' : 'pre'}}>
我正在使用 Material-UI v5 并尝试迁移到使用 styled
而不是 makeStyles
因为这似乎是“首选”方法现在。我知道使用 makeStyles
仍然有效,但我正在尝试采用新的样式解决方案。
我有一个代表导航链接的列表项列表,我想突出显示当前选中的那个。下面是我如何使用 makeStyles
:
interface ListItemLinkProps {
label: string;
to: string;
}
const useStyles = makeStyles<Theme>(theme => ({
selected: {
color: () => theme.palette.primary.main,
},
}));
const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
const styles = useStyles();
const match = useRouteMatch(to);
const className = clsx({ [styles.selected]: !!match });
return (
<ListItem button component={Link} to={to} className={className}>
<ListItemIcon>{children}</ListItemIcon>
<ListItemText primary={label} />
</ListItem>
);
};
(注意这里我使用 clsx 来确定 selected
样式是否应该应用于 ListItem
元素。)
如何使用 styled
实现此目的?这是我到目前为止想出的(注意:ListItemLinkProps
的界面没有改变,所以我没有在这里重复):
const LinkItem = styled(ListItem, {
shouldForwardProp: (propName: PropertyKey) => propName !== 'isSelected'
})<ListItemProps & LinkProps & { isSelected: boolean }>(({ theme, isSelected }) => ({
...(isSelected && { color: theme.palette.primary.main }),
}));
const ListItemLink = ({ to, label, children }: PropsWithChildren<ListItemLinkProps>) => {
const match = useRouteMatch(to);
return (
// @ts-ignore
<LinkItem button component={Link} to={to} isSelected={!!match}>
<ListItemIcon>{children}</ListItemIcon>
<ListItemText primary={label} />
</LinkItem>
);
};
那么,关于这个的两个问题:
这是执行条件样式的最佳方法吗?
另一个问题是我无法计算出
styled
声明的正确类型 - 我必须将// @ts-ignore
注释放在 [=24= 上方] 因为其类型的声明方式。
Material-UI v5 使用 Emotion 作为默认样式引擎,并在内部始终使用 styled
以使想要使用 styled-components 而不是的人更容易不必将两者都包含在捆绑包中的情感。
虽然 styled
API 适用于很多用例,但它似乎不太适合这个特定用例。有两个主要选项可以提供更好的 DX。
一个选择是使用新的 sx prop available on all Material-UI components (and the Box component can be used to wrap non-MUI components to access the sx
features). Below is a modification of one of the List demos 演示这种方法(自定义 ListItemButton
模拟您的 ListItemLink
的角色):
import * as React from "react";
import Box from "@material-ui/core/Box";
import List from "@material-ui/core/List";
import MuiListItemButton, {
ListItemButtonProps
} from "@material-ui/core/ListItemButton";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import InboxIcon from "@material-ui/icons/Inbox";
import DraftsIcon from "@material-ui/icons/Drafts";
const ListItemButton = ({
selected = false,
...other
}: ListItemButtonProps) => {
const match = selected;
return (
<MuiListItemButton
{...other}
sx={{ color: match ? "primary.main" : undefined }}
/>
);
};
export default function SelectedListItem() {
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
index: number
) => {
setSelectedIndex(index);
};
return (
<Box sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}>
<List component="nav" aria-label="main mailbox folders">
<ListItemButton
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItemButton>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItemButton
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItemButton>
</List>
</Box>
);
}
这种方法的唯一缺点是它目前 notably slower 比使用 styled
,但它仍然足够快,适合大多数用例。
另一种选择是通过 css prop 直接使用 Emotion。这允许类似的 DX(虽然使用主题不是很方便),但没有任何性能损失。
/** @jsxImportSource @emotion/react */
import * as React from "react";
import Box from "@material-ui/core/Box";
import List from "@material-ui/core/List";
import MuiListItemButton, {
ListItemButtonProps
} from "@material-ui/core/ListItemButton";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import InboxIcon from "@material-ui/icons/Inbox";
import DraftsIcon from "@material-ui/icons/Drafts";
import { css } from "@emotion/react";
import { useTheme } from "@material-ui/core/styles";
const ListItemButton = ({
selected = false,
...other
}: ListItemButtonProps) => {
const match = selected;
const theme = useTheme();
return (
<MuiListItemButton
{...other}
css={css({ color: match ? theme.palette.primary.main : undefined })}
/>
);
};
export default function SelectedListItem() {
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
index: number
) => {
setSelectedIndex(index);
};
return (
<Box sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}>
<List component="nav" aria-label="main mailbox folders">
<ListItemButton
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItemButton>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItemButton
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItemButton>
<ListItemButton
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItemButton>
</List>
</Box>
);
}
在我开发的应用程序中(我还没有开始迁移到 v5),我希望结合使用 styled
和 Emotion 的 css
function/prop。在它的性能稍微提高之前(我认为最终会发生),我很犹豫是否大量使用 sx
道具。尽管它在许多情况下执行得“足够快”,但当我有两个可用的类似 DX 选项并且其中一个的速度是另一个的两倍时,我发现很难选择较慢的那个。我选择 sx
道具的主要情况是我想为不同的断点或类似区域设置不同的 CSS 属性的组件,其中 sx
道具提供比其他道具更好的 DX选项。
相关回答:
这似乎对我使用 sx 有用
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
...
const isMobile = useMediaQuery(useTheme().breakpoints.down('md'));
...
<Divider sx={{ whiteSpace: isMobile ? 'normal' : 'pre'}}>