Skip to content
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

feature request: render props component for injecting styles (instead of HoC) #11257

Closed
jedwards1211 opened this issue May 6, 2018 · 10 comments
Labels
duplicate This issue or pull request already exists

Comments

@jedwards1211
Copy link
Contributor

I'm getting tired of HoCs for various reasons, including WebStorm being unable to automatically suggest HoC-wrapped components as imports. It seems like there's a big movement in the community away from HoCs toward render props components.

I made one for withStyles here: https://github.com/jcoreio/material-ui-render-props-styles

Would you like for me to add this component to material-ui itself?

import WithStyles from 'material-ui-render-props-styles'

const styles = theme => {
  root: {
    backgroundColor: theme.palette.primary.light,
  },
}

const PrimaryDiv = ({children}) => (
  <WithStyles styles={styles}>
    {({classes}) => (
      <div className={classes.root}>
        {children}
      </div>
    )}
  </WithStyles>
)
@oliviertassinari oliviertassinari added the duplicate This issue or pull request already exists label May 6, 2018
@oliviertassinari
Copy link
Member

I believe it's a duplicate of #11047.

@oliviertassinari
Copy link
Member

oliviertassinari commented May 6, 2018

I made one for withStyles here: https://github.com/jcoreio/material-ui-render-props-styles

We have been documenting a render-prop possible implementation. It's straightforward.
I also have some concern about the override story. I believe it doesn't work with your implementation: #11047 (comment).

@jedwards1211
Copy link
Contributor Author

Oh I see. Hmmm...that's a real bummer that it's a technical difficulty...almost defeats the purpose of using a render-prop implementation...

@jedwards1211
Copy link
Contributor Author

The problem of leaving things like this up to the user to implement is

  • people will fail to realize potential issues like I did here
  • people will put code like this in packages, just like I did, so that they don't have to copy-paste stuff between projects

It's such a small amount of code, I think it really makes more sense to put it in this project.

@jedwards1211
Copy link
Contributor Author

In any case this really highlights the hackiness of using counters for CSS in JS...maybe there's just no better way because of CSS' own design flaws, but time and time again I get frustrated how brittle counters are.

@oliviertassinari
Copy link
Member

It's such a small amount of code, I think it really makes more sense to put it in this project.

I have no strong objection. We started by documenting it to test how people would react. We only had good feedback so far.

this really highlights the hackiness of using counters for CSS in JS

What makes you think it's a counter problem? The problem is related to how React calls the hooks. It has to be in the right order. I don't think that it's a hard limitation. It can be worked around. But this requires time.

@jedwards1211
Copy link
Contributor Author

Maybe I'm a bit confused. As far as I understand:

  • the overriding <style> tag from the parent has to come after the overridden <style> tag from the child
  • but since React renders parents before children, normally the the parent's <style> tag would get attached before the child's <style> tag
  • so something is needed to ensure that the parent's <style> tag comes after the child's <style> tag... I thought the counter is used to ensure this?

In any case having to call createStyled outside of the render function somehow solves this, since the parent imports the child and the child calls its createStyled before the parent calls its createdStyled. Am I right? It's not exactly clear to me how it controls the <style> tag order.

To me this side effect of the order in which modules call withStyles is an undesirable impurity, whereas the way React calls parent hooks before child hooks doesn't seem like a flawed design.

Here's a thought: if the parent WithStyles could keep a reference to its <style> node, then the child could get that node via context, and then call document.head.insertBefore(myStyle, parentStyle). That would theoretically work regardless of the order in which withStyles is called.

@oliviertassinari
Copy link
Member

I thought the counter is used to ensure this?

@jedwards1211 We have two counters. The first one is used for this purpose and another one is used to guarantee classname uniqueness. I thought you were referring to the second counter as it's the one most people are aware of. The first counter is quite hidden.

Am I right?

Yes, you are right, the counter defines the index of the style in the DOM.

Here's a thought

I would also like to benchmark how styled-components is handling the issue.

@jedwards1211
Copy link
Contributor Author

The first counter is quite hidden

Yes, I only knew about it from reading through the withStyles code.

@jedwards1211
Copy link
Contributor Author

@oliviertassinari I was thinking about a React hooks API would have the same problem as this with style tag order and overrides.

I was thinking, if component A wraps and overrides some classes in component B, A will inject its style element first. But, if B can somehow find the style element for the overriding classes from A, then B could inject its style tag above A's tag.

I wish there were a DOM/CSSOM API to look up the <style> element for a given CSS class. If only there were then this would be pretty easy. But I'm pretty sure there's not (I just posted a question on Stack Overflow, so we'll see if anyone has an answer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants