-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hide implementation details from styles API #7636
Comments
I don't think that we should change the Regarding the The question is around how we are going to merge with react-jss. Material-UI support some unique feature that react-jss might not want to support. Let's take the We gonna need a wrapper at some point. |
cc @kof |
Also, this would be an important API change. I don't think that I will have the energy to go into, again, before v1. Maybe someone else might if we agree on a path going forward. I would hold that discussion until we move forward with |
|
@oliviertassinari other concerns aside, what I'm proposing here is not a change to the API with regards to All I'm suggesting is the addition of a small function - I can write this function and submit it here if you wish - it should be 7 lines of code or so. |
@Izhaki Right, but I don't think that we should add a If we make the assumption that no matter what we do with |
That makes sense. |
Hmm... just saw this on react-jss, which is exactly what I meant in my original post: import React from 'react'
import injectSheet from 'react-jss'
const styles = {
button: {
background: props => props.color
},
label: {
fontWeight: 'bold'
}
}
const Button = ({classes, children}) => (
<button className={classes.button}>
<span className={classes.label}>
{children}
</span>
</button>
)
export default injectSheet(styles)(Button) |
Well, shouldn't the view be that material-ui styles is an extension of react-jss, achieved using a wrapper (that potentially leaves the API intact when possible)? So is there a problem renaming Also, is this really such a large refactorisation? |
@Izhaki I would avoid using the same wording as
Could take 2-3 hours for the Material-UI codebase (yeah, 10 times less that what took me the style upgrade from alpha to beta) and then, users will have to upgrade their usage. |
Not sure I agree with this, unless you foresee someone using ramda.js, lodash, underscore all have But I guess there are pros and cons to each approach.
Luckily, you're still in pre-release phase. Having used the beta for a while now, I can't wait for the release, and given it being such a bounty, wouldn't mind any API changes. |
Once the need for users to call
|
I've become a big fan of using function as child instead of HOC lately (just like react router does for instance). So I would really like if there was API so I could do the following (instead of const styles = /* create stylesheet somehow */;
const MyComp = () => (
<Styled styles={styles}>
{classes => (
<div className={classes.someClass} />
)}
</Styled>
); It took me a while to get used to the syntax (used to hate it, but that can be said about JSX in general), but when I figured out it's uses it's really nice. For instance, you don't tie people up in the prop being called You can read more about it here: https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9 |
@Alxandr I was thinking about this interface too but haven't had time to decide on this. Ultimately having alternative interface is more harm than good so it needs to have a very serious reason besides of stylistic preferences or slightly smaller contracts. The biggest reason to start thinking about it was to provide a styling without HOC's because people often are incapable to understand HOC concept. The other thought is that HOC is part of react and people need to learn it. |
@kof another thing to consider is that one can easily be implemented in terms of the other, yet implementing a "function-as-children" component based on a HOC is rather painfull. // ignoring statics and all that jazz
const withStyles = (styles) => (BaseComponent) => {
return class StyledComponent extends Component {
render() {
return (
<Styled styles={styles}>
{classes => <BaseComponent classes={classes} {...this.props} />}
</Styled>
);
}
};
}; |
@kof While I find that the compound component API provides more flexibility over the Higher-order Component one, I also find the Higher-order Component API a game changer when needing to access the injected property in the life cycles. In our case, I think that the priority is the following "lifecycles > flexibility". Meaning I think that we should stick to the Higher-order component
@Izhaki That's right, we can always change the wording. I can post a question on Twitter, we will see the result :).
@hubgit That makes sense, we can change that too. But I don't think that we should change the component location. |
@oliviertassinari @hubgit I'm not sure about promoting I'm actually hiding MUI behind a module (so all app imports don't have |
@Izhaki We are already doing the following
I have been doing the same on a large project, though on small ones I'm consuming it directly. |
@oliviertassinari what do you mean you can access it in the lifecycles in a HOC and not with function as child? class MyComp extends Component {
constructor(props) {
super(props);
console.log(this.props.styles);
}
// and all the others
onComponentDidMount() {
console.log(this.props.styles);
}
}
const App = () => (
<Styled styles={myStyles}>
{styles => <MyComp styles={styles} />}
</Styled>
) [Edit] <jss(MyComp)>
<MyComp />
</jss(MyComp)> Which is exactly the same as you end up with if you have function as children. It's just how you get there that differs. There is no difference in lifecycle methods or any such (at least as far as I'm aware off, if I'm wrong please let me know). |
@Alxandr We won't go into this solution. To be more specific, I was referring to the collocation advantage with life cycles. You don't have to switch between files. |
@oliviertassinari Would love to see an example of this, maybe like a recipe in the docs. I have just started using material-ui and was a bit sceptical about CSS in JS, but I like it. |
@HriBB In my app, instead of import Grid from 'material-ui/Grid' I'll have import {
Grid
} from 'ui/controls' And export { default as Grid } from 'material-ui/Grid' |
@HriBB Here is an example of a wrapper above the import React from 'react'
import classNames from 'classnames'
import PropTypes from 'prop-types'
import MuiPaper from 'material-ui/Paper'
import { withStyles, createStyleSheet } from 'material-ui/styles'
const styleSheet = theme => ({
backgroundA100: {
background: theme.palette.accent.A100,
},
backgroundA200: {
background: theme.palette.accent.A200,
},
backgroundA400: {
background: theme.palette.accent.A400,
},
padding: {
padding: theme.spacing.unit,
},
})
function Paper(props) {
const { background, classes, className, padding, ...other } = props
return (
<MuiPaper
elevation={0}
square
className={classNames(
classes[`backgroundA${background}`],
{
[classes.padding]: padding,
},
className
)}
{...other}
/>
)
}
Paper.propTypes = {
background: PropTypes.oneOf(['100', '200', '400']),
classes: PropTypes.object.isRequired,
className: PropTypes.string,
padding: PropTypes.bool,
}
Paper.defaultProps = {
background: '100',
padding: false,
}
export default withStyles(createStyleSheet(styleSheet))(Paper) Also, as you can see, the plan with this issue is to be able to have the following diff on userland: -export default withStyles(createStyleSheet(styleSheet))(Paper)
+export default withStyles(styleSheet)(Paper) |
Options will be moved to the second argument: export default withStyles(styleSheet, { name: 'Paper' })(Paper) |
I've just added MUI v1 to another project. I've refactored the create-react-app on a fork to show where we ended up after refactoring. This is just to give some sense of a real use-case, albeit opinionated. Note that currently the example excludes the concept of HighlightsCompared to the original example: theme and stylesheet injectionWe inject both
const styleSheet = ( theme ) => ({
'@global': {
html: {
background: theme.palette.background.default,
WebkitFontSmoothing: 'antialiased', // Antialiasing.
MozOsxFontSmoothing: 'grayscale', // Antialiasing.
},
body: {
margin: 0,
},
},
})
const theme = {
palette: createPalette({
primary: purple,
accent: green,
}),
}
const Root = () => {
return (
<Router>
<Route path="/" component={ App } />
</Router>
)
}
export default withUI( Root, theme, styleSheet ) injectSheetWe have used
import styleSheet from './App.jss'
// ...
export default injectSheet( styleSheet, App )
export default
{
container: {
textAlign: 'center',
paddingTop: 200,
}
} Hiding MUIThat's more for @HriBB - don't think an actual example should include this.
import { Button } from '../ui/controls/';
import Dialog, {
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
} from '../ui/controls/Dialog';
import Typography from '../ui/Typography'; |
I have merged the first PR toward solving this issue. We should be able to remove |
Good job @oliviertassinari . As an additional note, if you look at context.js and withRoot.js in the refactor, you'll probably recognise that these are rather generic and unlikely to change between applications (note that I don't think I do wonder if these can be merged into MUI, rather than having to let clients write them time and again? Suffice to look at the repeation between the create-react-app example and the next one to see the generic patterns emerging. |
So yesterday I was looking into the css in js section in the v1 docs, and equally delved into withRoot, and other examples. Amazing job.
But I suspect, having done so myself many times in the past, that you guys have put so much effort and thought into this, that you fell a step short from perfecting the API.
That said from a client perspective.
Current API
Currently, the examples involves:
Followed by:
Possible improvements
Now consider you achieve the same thing with:
then (just an object)
or (a function in case
theme
is needed)or (a tuple in case the sytlesheet name is important - the second parameter may be a function or an object)
Followed by (given
applyStyleSheet
is curried):Basically,
applyStyleSheet
hides awaywithStyles
andcreateStyleSheet
, and has some switch logic to see if the style sheet provided is an object, function, or a tuple.props.classes?
Lastly, another part of the API, as seen in your own doc's AppContent involves:
withStyles
injects aclasses
property onprops
.It took a bit of mental mapping to understand that
classes.content
is actually the name of the class that is used in the stylesheet provided towithStyles
.It is nobody's fault ES6 has classes and CSS has classes.
I just wonder if using
styleSheetClasses
instead ofclasses
, albeit more verbose, would be clearer? As in:The text was updated successfully, but these errors were encountered: