目前,我正在开发一个需要道具和主题对象的组件。
起初,它适用于主题对象
const styles = theme => ({
title: {
...theme.typography.headline,
textAlign: 'center',
padding: '8px 16px',
margin: 0,
color: theme.palette.common.white,
backgroundColor: theme.palette.primary[500],
},
withStyles(styles, { withTheme: true })(Component);
....
但我还需要访问样式对象中的道具。
我试过例子但它不起作用。
{
....
display: (props) => props.display
}
我最终结合react-jss
和withTheme
来做到这一点
import { withTheme } from 'material-ui/styles';
import injectSheet from 'react-jss';
function withStyles(styles, Component) {
return withTheme()(injectSheet(styles)(Component));
}
export default withStyles;
....
const styles = {
title: {
display: (props) => props.display,
textAlign: 'center',
padding: '8px 16px',
margin: 0,
color: ({ theme }) => theme.palette.common.white,
backgroundColor: ({ theme }) => theme.palette.primary[500],
},
它有效,但我真的很想念
title: {
...theme.typography.headline,
我们应该能够通过使用与 react-jss 相同的上下文键来制作 Material-UI 来解决这个问题: https :
另外,看看#7633
我准备了一个带有 react-jss 互操作性示例的 PR。 我会将其添加到文档中。 抄送@kof
@oliviertassinari这是否意味着现在应该可以访问样式定义中的道具? 我不清楚如何...
@pelotom不, withStyles 无权访问这些属性。 但是考虑到有多少人要求此功能。 在错误修复之后,这是我可以优先考虑的事情。 您可以使用 injectSheet HOC,但它为多个问题打开了大门:内存泄漏、热重载损坏、没有classes
组合、没有内部引用访问、损坏的主题嵌套处理。 至少,这是我过去一直面临的一些问题,并激发了我的重写。 我认为这些问题将逐步得到解决。
@oliviertassinari听到您会考虑优先考虑这一点非常令人放心! 这将使自定义组件变得更加容易。 例如,我想要一个具有可配置大小(即宽度和高度以像素为单位)的复选框:
<CustomCheckbox size={16} />
如果我们可以在styles
访问props
styles
,这将非常简单:
const styles = {
root: {
width: props => props.size,
height: props => props.size
}
}
或者
const styles = props => ({
root: {
width: props.size,
height: props.size
}
})
进而:
const CustomCheckbox = ({size, classes}) => <Checkbox className={classes.root} />;
export default withStyles(styles)(CustomCheckbox);
现在,您对我们应该如何处理这些类型的用例有什么建议吗? 或者您是否有任何估计何时可以在使用 withStyles 时添加对访问道具的支持?
@nmchaves您的用例似乎非常适合内联样式方法,您可以在文档中找到一些相关信息。 常问问题
https://github.com/callemall/material-ui/blob/75a30061e76eae93c711ec202a2c7e4238a4f19a/docs/src/pages/style/SvgIcons.js#L38 -L44
谢谢@oliviertassinari ! 我希望我可以使用withStyles
来完成这个,但是内联样式会很好用。 事实上,你在这里+在文档中推荐它让我对这个决定非常有信心。 再次感谢!
能够将道具(图像源)传递给背景图像的样式会很好
我会包装withStyle
const withStylesProps = styles =>
Component =>
props => {
console.log(props);
const Comp = withStyles(styles(props))(Component);
// return <div>lol</div>;
return <Comp {...props} />;
};
const styles = props => ({
foo: {
height: `${props.y || 50}px`,
}
});
export default withStylesProps(styles)(
props => (
<div className={props.classes.foo} style={{ ...props.style, background: 'yellow' }}>
<h1>Hello!</h1>
</div>
)
);
演示: https :
(我很惊讶 ^ 没有任何ThemeProvider
和JssProvider
设置 https://codesandbox.io/s/q6v7krx6,啊它初始化它)
@caub它有效,但您需要对这种模式保持谨慎。 注入的 CSS 将随着组件实例的数量而增长。 它是#7633 的副本。 我没有深入研究这个话题。 但我相信@kof版本使用了一些性能优化。
@caub感谢分享!
@oliviertassinari在 react-jss 中有这个https://github.com/cssinjs/react-jss/blob/master/readme.md#dynamic -values ,我想知道为什么它不能在 material-ui 中使用? 我也理解你的观点,你说内联style
道具非常适合动态值,但在同一个地方拥有所有样式定义会更好。 还有https://github.com/airbnb/react-with-styles可以处理className
和style
以获得更高效的动态样式
我面临同样的问题有人可以帮助我吗
`从'反应'导入反应;
从 'prop-types' 导入 PropTypes;
从'material-ui/styles'导入{withStyles};
从'material-ui/Drawer'导入抽屉;
从 'material-ui/AppBar' 导入 AppBar;
从“material-ui/工具栏”导入工具栏;
从'material-ui/List'导入列表;
从'material-ui/Typography'导入排版;
从'material-ui/IconButton'导入IconButton;
从'material-ui/Hidden'导入隐藏;
从'material-ui/Divider'导入分隔线;
从“material-ui-icons/Menu”导入 MenuIcon;
import { mailFolderListItems, otherMailFolderListItems } from './tileData';
const drawerWidth = 240;
const 样式 = 主题 => ({
根: {
宽度: '100%',
高度:430,
marginTop:theme.spacing.unit * 3,
zIndex: 1,
溢出:'隐藏',
},
应用框架:{
位置:'相对',
显示:'弹性',
宽度: '100%',
高度:'100%',
},
应用栏:{
位置:'绝对',
marginLeft:抽屉宽度,
[theme.breakpoints.up('md')]: {
宽度: calc(100% - ${drawerWidth}px)
,
},
},
导航图标隐藏:{
[theme.breakpoints.up('md')]: {
显示:'无',
},
},
drawerHeader: theme.mixins.toolbar,
抽屉纸:{
宽度:250,
[theme.breakpoints.up('md')]: {
宽度:抽屉宽度,
位置:'相对',
高度:'100%',
},
},
内容: {
背景颜色:theme.palette.background.default,
宽度: '100%',
填充:theme.spacing.unit * 3,
高度:'计算(100% - 56px)',
边距顶部:56,
[theme.breakpoints.up('sm')]: {
高度:'计算(100% - 64px)',
边距顶部:64,
},
},
});
导出类 ResponsiveDrawer 扩展 React.Component {
状态 = {
移动打开:假,
};
handleDrawerToggle = () => {
this.setState({ mobileOpen: !this.state.mobileOpen });
};
使成为() {
const { 类,主题 } = this.props;
const drawer = (
<div>
<div className={classes.drawerHeader} />
<Divider />
<List>{mailFolderListItems}</List>
<Divider />
<List>{otherMailFolderListItems}</List>
</div>
);
return (
<div className={classes.root}>
<div className={classes.appFrame}>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={this.handleDrawerToggle}
className={classes.navIconHide}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Responsive drawer
</Typography>
</Toolbar>
</AppBar>
<Hidden mdUp>
<Drawer
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={this.state.mobileOpen}
classes={{
paper: classes.drawerPaper,
}}
onClose={this.handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden smDown implementation="css">
<Drawer
variant="permanent"
open
classes={{
paper: classes.drawerPaper,
}}
>
{drawer}
</Drawer>
</Hidden>
<main className={classes.content}>
<Typography noWrap>{'You think water moves fast? You should see ice.'}</Typography>
</main>
</div>
</div>
);
}
}
ResponsiveDrawer.propTypes = {
类:PropTypes.object.isRequired,
主题:PropTypes.object.isRequired,
};
导出默认 withStyles(styles)(ResponsiveDrawer);
`
注入的 CSS 将随着组件实例的数量而增长。
@oliviertassinari注入的 CSS 会增长 +- html 会随着内联样式的增长而增长。 静态样式在单独的工作表中呈现并在所有组件实例中重复使用。
我确实喜欢这个,尽管使用无状态组件它会将withStyle
从render
重新渲染为render
,我们可以通过使用完整的纯组件来避免。
import React from 'react';
import {
withStyles,
Grid,
CircularProgress
} from 'material-ui';
const PreloadComponent = props => {
const { classes,size } = props;
return (
<Grid className={classes.container} container justify={'center'} alignItems={'center'}>
<CircularProgress size={size}/>
</Grid>
)
};
const StyleWithThemeProps = (props) => {
return withStyles(theme => ({
container: {
paddingTop: props.size*2 || 50,
paddingBottom: props.size*2 || 50,
}
}),{withTheme: true})(PreloadComponent)
};
const Preload = props => {
const { size } = props;
const WithStylesPreloadComponent = StyleWithThemeProps(props);
return (
<WithStylesPreloadComponent {...props}/>
)
};
Preload.defaultProps = {
size: 20
};
export default Preload;
我们可以使用完整的纯组件来避免更新
const PreloadComponent = props => {
const { classes,size } = props;
return (
<Grid className={classes.container} container justify={'center'} alignItems={'center'}>
<CircularProgress size={size}/>
</Grid>
)
};
const StyleWithThemeProps = (props) => {
return withStyles(theme => ({
container: {
paddingTop: props.size*2 || 50,
paddingBottom: props.size*2 || 50,
}
}),{withTheme: true})(PreloadComponent)
};
class PreloadFull extends React.PureComponent {
constructor(props,context) {
super(props);
}
componentWillMount() {
this.StyledPreloadFull = StyleWithThemeProps(this.props);
}
componentWillUpdate(nextProps) {
this.StyledPreloadFull = StyleWithThemeProps(nextProps);
}
render() {
const { StyledPreloadFull,props } = this;
return (
<StyledPreloadFull {...props}/>
);
}
}
PreloadFull.defaultProps = {
size: 20
};
export default PreloadFull;
@up209d它有效,但它很痛苦,我会尝试修改withStyles
,以更直接地使用https://github.com/cssinjs/react-jss ,它可以在值中传递道具
@SrikanthChebrolu你能不能把你的消息移到另一个问题上,因为它不是主题?
只是好奇这是什么状态? 我一直在阅读这个问题、JSS 文档、material-ui 文档,但还没有找到不需要我使用内联样式的 Mui+Jss+TypeScript 的解决方案。 放置一些内联样式有时是不可避免的,但在我的情况下,有多种样式具有许多不同的状态,所有样式都依赖于主题和道具:失望:
@chazsolo嘿Chaz,您实际上可以使用injectSheet
react-jss
injectSheet
而不是withStyles
的mui
。 通过这种方式,您可以同时拥有props
和theme
。
import injectSheet from 'react-jss';
const styles = theme => ({
container: {
color: props => theme.palette[props.color || 'primary'].main
}
});
...
export default injectSheet(styles)(AnyComponent);
import { JssProvider, jss, createGenerateClassName } from 'react-jss/lib';
import { MuiThemeProvider } from 'material-ui';
const generateClassName = createGenerateClassName();
...
<JssProvider jss={jss} generateClassName={generateClassName}>
<MuiThemeProvider theme={props.theme} sheetsManager={new Map()}>
<App/>
</MuiThemeProvider>
</JssProvider>
@chazsolo我想你想关注这个问题https://github.com/cssinjs/jss/issues/682
感谢@kof和@up209d - 订阅并放弃了 209d 的示例。
@up209d
不幸的是,我认为这对我不起作用 - 我已经实现了你的建议,并且我可以在styles
对象内看到函数调用中的道具,但我继续遇到错误。 我只是缺少类型吗? 我在 props 接口中扩展WithStyles
所以我可以访问 props 中的classes
对象(现在我想知道这是否是 https://github.com/mui- 中引用的问题) org/material-ui/issues/8726#issuecomment-337482040)
TS2344: Type '(theme: ITheme) => { arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcM...' does not satisfy the constraint 'string | Record<string, CSSProperties> | StyleRulesCallback<string>'.
Type '(theme: ITheme) => { arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcM...' is not assignable to type 'StyleRulesCallback<string>'.
Type '{ arc: { stroke: string; strokeWidth: (props: any) => string | number; }; arcMovement: { strokeDa...' is not assignable to type 'Record<string, CSSProperties>'.
Property 'arc' is incompatible with index signature.
Type '{ stroke: string; strokeWidth: (props: any) => string | number; }' is not assignable to type 'CSSProperties'.
Types of property 'strokeWidth' are incompatible.
Type '(props: any) => string | number' is not assignable to type 'string | number | undefined'.
Type '(props: any) => string | number' is not assignable to type 'number'.
我的主题看起来像:
import { ITheme } from '...';
export default (theme: ITheme) => ({
arc: {
// ...
strokeWidth: (props: any): number | string => {
// this logs the correct data I'm expecting
console.log(props.data[0].properties.name)
return 1.5
}
},
arcMovement: {
// ...
},
})
有趣的是,当我在组件中使用classes
对象时, arc
和arcMovement
是有效的属性:
// from Chrome console
{
arc: "Arcs-arc-0-2-1 Arcs-arc-0-2-3",
arcMovement: "Arcs-arcMovement-0-2-2"
}
更新
我能够让这个工作,但正如上面的评论中所指出的,我不得不删除所有对WithStyles
、 withStyles
引用,并且我失去了classes
组成和主题嵌套。 我现在要休息一下,注意线程。 感谢所有的帮助!
@chazsolo嘿查兹,我不知道,但要获得classes
内props
的的style
对象。 如果是这样,我认为这是不可能的,因为classes
仅在jss
处理了style
对象后才可用,如何在制作过程之前访问classes
classes
甚至还没有被触发?
我认为@caub已经提供了解决方案。 只需稍加改动即可重新发布解决方案。 不需要任何额外的库。
构建您自己的包装器withStylesProps
。
import { withStyles } from 'material-ui/styles';
const styles = ( theme, props ) => ({
exampleStyle: {
color: 'red' // <-- or try theme.palette.primary[600]
}
})
const withStylesProps = ( styles ) =>
Component =>
props => {
const Comp = withStyles(theme => styles(theme, props))(Component);
return <Comp {...props} />;
};
const YourComponent = ({ classes }) =>
<Typography type="display4" className={classes.exampleStyle}>{type}</Typography>
export default withStylesProps(styles)(YourComponent);
如果您不喜欢为每个组件创建withStylesProps
,请尝试将其添加到单独的文件中并导入到您想要的任何位置。
@iamthuypham感谢您的提示。 但是,当我用withStylesProps
包裹我的组件时,我在包裹组件内某处使用的过渡组件<Collapse
的动画停止工作。
@jdolinski1你能复制/粘贴你的代码示例吗?
@iamthuypham您的解决方案的缺点是每次创建组件时都会创建一个新的<style>
标签。 此外,在使用 defaultProps 并将它们添加到您的 HOC 组件而不是基础组件时,您可能要小心。
所有这些都由react-jss
支持,它不能由material-ui
本地支持吗?
另外,我认为@jdolinski1的问题是您的代码不会传播包装组件可能具有的children
。
@iamthuypham我认为不建议这样做,因为我过去曾经这样做过,只要应用程序增长很快,您可能会遇到性能不佳的情况。 使用新的 jss style
对象创建component
的新实例在编码原则方面并不好,因为style
对象必须一次又一次地完全重新渲染,每个每props
变化的时间。 使用injectSheet
的react-jss
是更好的选择。 如果你查看injectSheet
你会发现它把你的style
对象分成两部分( static
和dynamic
)所以只有dynamic
在props
更改时重新渲染。
如何使用jss-nested 和injectSheet 等插件?。
使用 injectSheet 我无法让 '&:hover' 语句起作用。
使用 withStyles 我无法访问道具...
@koutsenko这是一个例子:
import React from "react";
import { makeStyles } from "@material-ui/styles";
import Button from "@material-ui/core/Button";
const useStyles = makeStyles({
root: {
background: props => props.color,
"&:hover": {
background: props => props.hover
},
border: 0,
borderRadius: 3,
color: "white",
height: 48,
padding: "0 30px"
}
});
export default function Hook() {
const classes = useStyles({
color: "red",
hover: "blue"
});
return <Button className={classes.root}>Hook</Button>;
}
https://codesandbox.io/s/pw32vw2j3m
我希望它有帮助。
哇,我们在大约 1 年内取得的进步真是太神奇了😍。
现在你如何打字?
@stunaz好问题。 我不知道。 我没有仔细研究过。 @eps1lon已经完成了模块的 TypeScript 定义。 您可以将其用作起点。
https://github.com/mui-org/material-ui/blob/f4281a77d15b0d6eec9d33cdc358cfb89844996d/packages/material-ui-styles/src/index.d.ts#L72
@koutsenko这是一个例子:
感谢@oliviertassinari ,“ react@next ”现在可以使用了。
@koutsenko如果您无法使jss-nested
工作,那一定是您编码中某处的配置问题。 由于jss-nested
包含在jss-default-preset
,因此它仅适用于 oob
@oliviertassinari
您还可以使用 props 为给定的选择器设置整个样式对象吗? 在哪里可以有条件地申请房产?
例如,像这样
withStyles({
root: {
'& > path': (props) => {
if(props.color)
return {
fill: props.color
};
return {};
}
}
})
所以,如果道具不存在,那么它使用以前的填充值,而不是我必须设置的其他值? 例如,还有其他规则通常适用于填充,但如果设置了color
属性,我只想设置这个新的填充属性。
谢谢!
@Guardiannw由于某种原因,您的变体不起作用。 也许@kof可以让我们了解为什么 💡。 您可以执行以下操作之一:
// 🏆
const useStyles = makeStyles({
root: {
"& > span": {
backgroundColor: props => props.color || null,
}
}
});
// or
const useStyles = makeStyles({
root: props => ({
"& > span": {
backgroundColor: props.color || null
}
})
});
@oliviertassinari我很难让你的第二个选项与withStyles
函数一起工作。 它只适用于makeStyles
和钩子吗?
@Guardiannw它与@material-ui/styles
任何 API 一起使用。
@oliviertassinari看起来像一个有效的语法,在 v10 中添加了 fn 值,所以要么使用了 v9,要么我需要一个代码和框复制
好的,这就是我试过的。 可能得再试一次。
@oliviertassinari我有一个关于@materia-ui/styles 的使用的问题,它是否可用并在生产环境中使用?,在文档中表明它不适用于稳定版本,我正在使用“ 3.9.1",您提供的示例https://github.com/mui-org/material-ui/issues/8726#issuecomment -452047345 它具有我需要的强大而有用的功能。 在这些问题中,我从不同的角度看到了许多评论,我也喜欢 @caub 的解决方案https://github.com/mui-org/material-ui/issues/8726#issuecomment -363546636 ,但您对他的评论解决方案很好。
@contrerasjf0 @material-ui/styles
仅作为 alpha 版本提供。 我们像对待 React 生态系统中的大多数包一样对待 alpha 版本。 我建议你永远不要在生产中使用任何 alpha 包。 如果你这样做,你应该期待任何版本之间的错误和破坏性变化,即你应该能够处理添加的流失 alpha 版本。
我希望人们在业余项目中使用这些版本,或者在未部署到生产但仍像生产分支一样经过测试的单独分支上使用它。 我非常感谢使用这些 alpha 版本并为我们提供反馈的每个人。
@up209d是的,您的解决方案有效,但是使用
styles = { name: { cssprop: props => {} }
符号,不是styles = props => ({ name: { cssprop: {} })
此外, JssProvider 不是必需的。
@koutsenko
// at value level:
styles = { name: { cssprop: props => value }
styles = theme => ({ name: { cssprop: props => value })
// at class name level
styles = { name: props => ({ cssprop: value }) }
styles = theme => ({ name: props => ({ cssprop: value }) })
您不能在顶层访问props
,即使作为theme
之后的第二个参数
我找到了一个方法
// MyComponent.tsx
import React, { PureComponent } from 'react';
import { myComponentWithStyles } from './myComponentWithStyles';
export interface MyComponentProps {
copy: string;
size?: number;
}
export class Twemoji extends PureComponent<myComponentWithStyles> {
public render() {
const { copy, classes } = this.props;
return (
<div className={classes.message}>
{copy}
<img src="https://via.placeholder.com/150" />
</div>
);
}
}
// myComponentWithStyles.tsx
import React from 'react';
import { withStyles, WithStyles, Theme } from '@material-ui/core';
import { MyComponent, MyComponentProps } from './my-component';
const styles = (props: Theme & MyComponentProps) => ({
message: {
fontSize: props.typography.caption.fontSize,
'box-sizing': 'content-box',
'& img': {
width: `${props.size || 24}px`,
height: `${props.size || 24}px`,
padding: '0 4px',
verticalAlign: 'middle',
},
},
});
export type myComponentWithStyles = WithStyles<any>;
export const Component = (props: MyComponentProps) => {
const StyledComponent = withStyles((theme: Theme) => styles({ ...props, ...theme }))(
MyComponent
);
return <StyledComponent {...props} />;
};
md5-d0e1b51e375682cf2aad9c4d66b6c73a
<Component size={12} />
@andreasonny83避免这种模式。 我们在 v4 中提供了一个原生 API。
@oliviertassinari感谢您的更新。 那个模式已经可用了吗? 有什么可用的文档吗?
https://next.material-ui.com/css-in-js/basics/#adapting -based-on-props
最后一个问题@oliviertassinari 。 我可以将makeStyles
与withStyles
结合使用吗?
我找不到相关的文档。 我想要做的是:
const useStyles = makeStyles({
message: {
boxSizing: 'content-box'
}
});
export const ComponentWithStyles = withStyles(useStyles())(MyComponent);
@andreasonny83
makeStyles
用于 react-hookswithStyles
HOC 接受相同的语法使用其中之一,在您的示例中只需删除makeStyles
:
const styles = { message: {boxSizing: 'content-box', background: props => props.bg} };
export const ComponentWithStyles = withStyles(styles)(MyComponent);
Gday 的人认为我参考上述讨论分享了我当前的解决方案,希望它可以帮助某人或某人对我当前的解决方案提供更好的建议。 对于我的登录页面 id 就像一个随机的背景图片,但 id 仍然喜欢保持材料 ui api 的力量。 AuthPage 只是父表示层,它将各个身份验证组件(登录、锁定、忘记密码、密码重置等)作为子级。 可以确认每个页面刷新一个新的背景加载以及 AuthPageContainer 道具中的一个很好的强类型道具
// AuthPage.styles.tsx
import { Container } from "@material-ui/core";
import { ContainerProps } from "@material-ui/core/Container";
import { withStyles } from "@material-ui/core/styles";
import React from "react";
interface IAuthContainerProps extends ContainerProps {
background: string;
}
export const AuthContainer = withStyles({
root: props => ({
alignItems: "center",
backgroundImage: `url(${props.background})`,
backgroundPosition: "50% 50%",
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
display: "flex",
height: "100vh",
justifyContent: "center",
margin: 0,
padding: 0,
width: "100%"
})
})((props: IAuthContainerProps) => <Container maxWidth={false} {...props} />);
// AuthPage.tsx
import React from "react";
import forest from "../../assets/backgrounds/forest.jpg";
import sky from "../../assets/backgrounds/sky.jpg";
import uluru from "../../assets/backgrounds/uluru.jpg";
import { AuthContainer } from "./AuthPage.styles";
const AuthPage = ({ children }) => {
const generateBackground = () => {
const backgrounds = [forest, sky, uluru];
const index = Math.floor(Math.random() * backgrounds.length);
return backgrounds[index];
};
return (
<AuthContainer background={generateBackground()}>{children}</AuthContainer>
);
};
export default AuthPage;
简单地做这样的事情:
// styles.js
export default theme => ({
root: props => ({
// some styles
}),
...
});
//container.js
export default withStyles(styles)(MyComponent);
通过也状态怎么样?
@luky1984
你不能。 相反,您可以这样做:
// Component.js
<Button
className={`
${classes.button}
${this.state.isEnable
? classes.enable
: classes.disable}
`}
/>
或者使用 clsx https://www.npmjs.com/package/clsx代替
最有用的评论
@oliviertassinari听到您会考虑优先考虑这一点非常令人放心! 这将使自定义组件变得更加容易。 例如,我想要一个具有可配置大小(即宽度和高度以像素为单位)的复选框:
如果我们可以在
styles
访问props
styles
,这将非常简单:或者
进而:
现在,您对我们应该如何处理这些类型的用例有什么建议吗? 或者您是否有任何估计何时可以在使用 withStyles 时添加对访问道具的支持?