-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
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
Proposal: mui-themeable higher order component #2493
Conversation
About time someone addressed this :D Thanks a lot. @oliviertassinari I think this should be merged asap so while we work on the docs we adept the components too. |
@newoga Could you be so kind and provide a Oh and document it too :D sorry 😅 |
@subjectix Sure, that makes sense. I should have time later tonight or tomorrow to whip up a first iteration of a And no apologies on documentation, I like documentation and I'm here to help 😄 |
Note: Just wanted to write this so I don't forget when I come back to this. In this pull request's implementation, the Maybe something like: /* excerpt from mui-themeable.js */
const MuiComponent = (props, {contextTheme = ThemeManager.getMuiTheme(DefaultRawTheme)}) =>
{
const {
muiTheme = contextTheme
} = props;
return <WrappedComponent {...props} muiTheme={muiTheme} />;
}; Maybe there's a nicer way to write that. |
I would argue that it's more explicit to wrap that component they want to customize around the same Besides if the theme is not provided through context, but rather through a prop, the how will the Children of that component look like? they won't know the theme was changed for their parent. they will still try to get it from the context (which is inherited from the ancestor). |
@subjectix That's a good point, I hadn't considered that and I think you're right on all accounts. I'm taking a stab at the |
Thanks @newoga, I really like your approach 👍 This is the right way to do this 👍 😄 |
Thanks @subjectix! This should do it. Quick question, I wrote the I confirmed that this is more or less working locally, but I'm going to try to create some official examples in docs and push again. I might have some other ideas and concerns (nothing show stopping) but I'll experiment a bit more and report back soon. |
@newoga Not to be sour (because I love this), but should this perhaps be implemented without using a stateless functional component? There are still some issues with these when it comes to hot reloading (more here). |
@neverfox, thanks for the feedback and pointing that out! I tend to write components stateless and functional by default until there's a reason to not to 😅 . That could very well be a good enough reason not to and am open to switching it to a class implementation. It seems like stateless functional components are being pushed and encouraged across the board, but tooling for it hasn't fully caught up for it yet. Let me know if you have any other thoughts. |
@newoga I do too, since I build all of my apps with redux. My only thought about why this isn't going to be a problem is that this isn't code that a developer would necessarily be changing in development (so nothing to hot reload). If my thinking is correct on that, then my concern is probably misplaced. Unfortunately, it is a reason why I don't currently use the pattern for my own app components yet. |
Out of curiosity @neverfox, do you know if the implications of using a stateless functional component in this particular case only relevant for hot reloading when changing material-ui source code? Or does it impact editing any source code in user space that includes material-ui components? Let me know if that question makes sense. |
Haha! We both thought of it at the same time. |
@newoga I don't know for sure, but I would expect that it wouldn't be an issue if you weren't modifying material-ui source on-the-fly because the stateless material-ui component would re-render normally when and if props change, but that is something to explore. |
Right, I was thinking the same. But you're right, this is something to explore. Before we merge this, I'll try to experiment to see what impact this could have in that area. I know |
This is like an ahead-of-time conversion :D no need to make it es5, or rather, it's better this way. (less work to do on the es6-classes branch :D ) @neverfox Thanks for the feedbacks, I think we shouldn't worry about hot reloading in this case. these classes are very small. the only place for stateless functional components (I HATE THAT NAME :/ ) are utils like this, examples and very tiny components. all the other are going to be es6-classes. besides, at the worst case it's only a reload :D @newoga I'll review your code in a moment and provide feedback 😁 |
@newoga @subjectix Thanks, and I really appreciate the friendly tone around here. |
muiTheme: PropTypes.object, | ||
}; | ||
|
||
ThemeProvider.childContextTypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't these (and propTypes
) be static members of the ThemeProvider
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we should be able to do that if we have the right babel stage/preset. I'll give it a try and if it works I'll move it!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, see if it works, if not fall back, take cover :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I tried making them static members, the eslinter doesn't detect that muiTheme
and children
as propTypes, and produces an error...I noticed we're not using the latest versions of some eslint related packages. I can investigate a bit...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you can't find anything just disable the faulted rules for this file. (with a tiny comment saying why) Thanks a lot.
@@ -51,7 +42,6 @@ const Divider = ({inset, style, ...other}, {muiTheme = ThemeManager.getMuiTheme( | |||
|
|||
Divider.propTypes = propTypes; | |||
Divider.defaultProps = defaultProps; | |||
Divider.contextTypes = contextTypes; | |||
Divider.childContextTypes = childContextTypes; | |||
Divider = muiThemeable(Divider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will have no name, please add a displayName member 😁
@subjectix Just letting you know, I haven't abandoned this PR yet, still working on cleaning this up and fixing the issues you highlighted. I had a separate idea/branch relating to this that I decided to present as PR #2509. Feel free to review when it's convenient. Thanks! |
Alright, I was able to make the Still couldn't get this to work:
The error seems to be coming from the parse method from |
@newoga I see, then never mind, seems like the P.S. It's all good, thanks a bunch 👍 👍 @oliviertassinari Do a final review on this one. |
return <WrappedComponent {...props} muiTheme={muiTheme} />; | ||
}; | ||
|
||
MuiComponent.displayName = `mui(${getDisplayName(WrappedComponent)})`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering. Some of our components rely on the displayName to handle composition correctly.
Aren't going to break this by adding the mui(X)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, that's gonna happen! Maybe we shouldn't relay on display name, doesn't instanceof
work with es6-classes? is it does I think we should use instanceof instead. hell of a lot safer!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'd have to look at how other components are using displayName
currently. I'm not sure if this makes a difference, but this function isn't changing the name of the component it's wrapping, it's just changing the name of the high order "wrapping" component to be mui(NameOfWrappedComponent)
. So the X
component still lives in the tree underneath this and is appropriately named X
.
I haven't tested this approach beyond the Divider component we merged recently (which is a very simple component). Later in a this branch I'll convert some of the more complex components and those that use displayName to use muiThemeable and investigate the impact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's just changing the name of the high order "wrapping" component to be mui(NameOfWrappedComponent)
I think that's the issue. The parent component will just see the HOC one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, got you. I'm not sure or remember what the use cases are in which components are looking at the displayName
of a child component. I'll investigate a bit more. Slowly but surely I'll get a better grasp of the codebase :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm, just to play devil's advocate, for this particular use case, could it make sense to just clone any element that is passed, and pass a prop that dictates that it belongs to the app-bar
, and it's up to the icon-element (whether it's a IconMenu
, IconButton
, or FlatButton
to dictate how style change given that it is in an app-bar
?
That way if there are new types of components that support being a icon-element
, we don't have to add them to the switch condition AppBar
.
I don't know if I agree with myself on that, but that could be a strategy to remove coupling between the components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to propose this again. Isn't it better to inverse the control? I mean instead of having parents looking for particular children, they provide a contextual interface for the children to subscribe themselves. This will allow arbitrary nesting of related components without breaking logic. I'll provide a Proposal PR, (like the ones @newoga does) to clarify my point. It will be with the DropDownMenu.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@subjectix Sounds good to me. I agree with looking at that approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, can we set the displayName of hoc component equal to the child component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we can definitely do that.
nice work @newoga. squash please 😁 @oliviertassinari I'm good with this. feel free to merge ^_^ |
13bcdfd
to
fa4f480
Compare
@oliviertassinari @subjectix Thanks a lot for your feedback and help on this 😃 Just squashed! Just wanted to let you know:
Thanks! |
@newoga Really nice work! Your contributions to this library are game changing 😎
@oliviertassinari Thoughts? |
@subjectix I agree with you. |
Proposal: mui-themeable higher order component
@newoga Thanks a lot 👍 🎉 🎉 |
No problem! 🎉 😄 |
I'm just opening this up for discussion, something we could consider at a later date.
The React documentation on context says:
This pull request shows an approach to implementing context for the purpose of themes in a higher order
mui-themeable.js
component. It allows us to develop material-ui components without having to use context directly, and instead receive themuiTheme
as a prop (even though the higher order component is providing themuiTheme
prop from context). I also changed the currentDivider.jsx
implementation as an example of how it's used.Note: This current implementation technically should work as a
@muiThemeable
decorator on classes as well, but sinceDivider.jsx
is a function, I couldn't show that as an example.