Skip to content

Conversation

@SimonDegraeve
Copy link
Contributor

You can use it to set the theme and the context required by material-ui.

There is two ways to use it.

  1. High order component
// ES6 syntax

import React from 'react';
import {Theme, Styles, RaisedButton} from 'material-ui';

const myTheme = {
  getPalette: () => ({
    accent1Color: Styles.Colors.deepOrange500
  }),
  getComponentThemes: () => ({})
};

export default class App extends React.Component {
  render() {
    return (
      <Theme theme={myTheme}>
        {props => // props contains the ThemeManager instance in "themeManager" property (also added to context)
          <RaisedButton label='Super Button' primary={true}/>
        }
      </Theme>
    );
  }
}
  1. Decorator
// ES6 syntax

import React from 'react';
import {Styles, RaisedButton} from 'material-ui';
import {theme} from 'material-ui/lib/theme';

const myTheme = {
  getPalette: () => ({
    accent1Color: Styles.Colors.deepOrange500
  }),
  getComponentThemes: () => ({})
};

@theme(myTheme)
export default class App extends React.Component {
  render() {
    return (
      <RaisedButton label='Super Button' primary={true}/>
    );
  }
}

@troutowicz
Copy link
Contributor

👍 HOC

@SimonDegraeve
Copy link
Contributor Author

@hai-cea,

Done.
I also plan to add HOCs to replace the mixins (or at least provide an alternative)... Will open another PR soon.

hai-cea added a commit that referenced this pull request Jun 9, 2015
Add "Theme" high order component and "theme" decorator
@hai-cea hai-cea merged commit 26081eb into mui:master Jun 9, 2015
@hai-cea
Copy link
Member

hai-cea commented Jun 9, 2015

Thanks @SimonDegraeve

@CumpsD
Copy link
Contributor

CumpsD commented Jun 21, 2015

Is there any way we can do this without having to do this weird construct:

{props => // props contains the ThemeManager instance in "themeManager" property (also added to context)
          <RaisedButton label='Super Button' primary={true}/>
        }

For example, in my app this results in this:

    app.router.run(function (Handler, state) {
        app.routeActions.activate(state);

        React.render(
            <Theme theme={theme}>
                {props =>
                    <ApplicationContainer app={app} {...props}>
                        <Handler {...state} />
                    </ApplicationContainer>
                }
            </Theme>, document.body);
      });

I realise this is needed, because of this part of your code:

    return this.props.children({
      muiTheme: this.themeManager.getCurrentTheme(),
      muiThemeManager: this.themeManager
    });

Would it work (no React expert here, yet....) if we set them on the children directly? Instead of having to wrap it in a function.

Something like https://github.com/martyjs/marty-lib/blob/e2640804254e796dd9ce03332e05239a2881c24d/modules/application/applicationContainer.js

I think it would look like this:

render: function render() {
      var _props = this.props;
      var children = _props.children;

      if (children) {
        if (isArray(children)) {
          return React.createElement(
            'span',
            null,
            React.Children.map(children, cloneWithTheme)
          );
        } else {
          return cloneWithTheme(children);
        }
      }

      function cloneWithTheme(element) {
        return React.createElement(element.type, extend({
          muiTheme: this.themeManager.getCurrentTheme(),
         muiThemeManager: this.themeManager
        }, element.props));
      }
    }

Then it would look nice if the result was something like:

    app.router.run(function (Handler, state) {
        app.routeActions.activate(state);

        React.render(
            <Theme theme={theme}>
                <ApplicationContainer app={app}>
                    <Handler {...state} />
                </ApplicationContainer>
            </Theme>, document.body);
    });

@CumpsD
Copy link
Contributor

CumpsD commented Jun 21, 2015

Guess what, I just tried it, and my guess worked:

  render: function render() {
      var _props = this.props;
      var children = _props.children;

      if (children) {
          children.props.muiTheme = this.themeManager.getCurrentTheme();
          children.props.muiThemeManager = this.themeManager;

          return React.createElement(children.type, children.props);
      }

    // return this.props.children({
    //   muiTheme: this.themeManager.getCurrentTheme(),
    //   muiThemeManager: this.themeManager
    // });
  }

Or even:

  render: function render() {
    var _props = this.props;
    var children = _props.children;

    if (children) {
      return React.createElement(children.type, _extends({
        muiTheme: this.themeManager.getCurrentTheme(),
        muiThemeManager: this.themeManager
      }, children.props));
    }
  }

Now I got rid of the function in my app code's render which makes it clean.

Should I create another PR to discuss and improve on this?

@CumpsD
Copy link
Contributor

CumpsD commented Jun 21, 2015

Actually, I'll leave it here, I whipped this up for my case (without the decorator) which seems to work nicely:

import React from 'react';
import { Styles } from 'material-ui';
import extend from './../../vendor/lodash/object/extend';

var ThemeManager = Styles.ThemeManager;

class Theme extends React.Component {
    getChildContext() {
        return {
            muiTheme: this.themeManager.getCurrentTheme(),
            muiThemeManager: this.themeManager
        };
    }

    componentWillMount() {
        this.themeManager = new ThemeManager();

        if (this.props.theme) {
            this.themeManager.setTheme(this.props.theme);
        }
    }

    render() {
        var _props = this.props;
        var children = _props.children;

        if (children) {
            return React.createElement(children.type, extend({
                muiTheme: this.themeManager.getCurrentTheme(),
                muiThemeManager: this.themeManager
            }, children.props));
        }
    }
}

Theme.displayName = 'Theme';

Theme.propTypes = {
    theme: React.PropTypes.object
};

Theme.childContextTypes = {
    muiTheme: React.PropTypes.object.isRequired,
    muiThemeManager: React.PropTypes.object.isRequired
};

export default Theme;

And my app now has:

import Theme from './util/theme-component';
import theme from './theme';

    log('Running React.render with router.');
    app.router.run(function (Handler, state) {
        app.routeActions.activate(state);

        React.render(
            <Theme theme={theme}>
                <ApplicationContainer app={app}>
                    <Handler {...state} />
                </ApplicationContainer>
            </Theme>,
            document.body);
    });

Which actually makes me happy, since it's now a nice higher order component without having to depend on a function.

@SimonDegraeve
Copy link
Contributor Author

Indeed I could use something like this.
I used the "function as children" pattern because it is more explicit and modular for the end-user. I don't mind if the owner of the repo wants to change it ;-)

@CumpsD
Copy link
Contributor

CumpsD commented Jun 21, 2015

Ah yes, that looks even nicer, safer checking :)

@jgoux
Copy link
Contributor

jgoux commented Jun 22, 2015

Hello,
I think I'm not getting how to properly use this feature.
Basically, I just want to define my theme once (so I think wrapping the top component with Theme is the way to go), and then use Material-UI component in my sub-components without having to deal with their Context and muiTheme objects. Is it possible ?

For example, App is my top-component

import React from "react"
import { Theme } from "material-ui"

export default class App {

    render() {
        return (
            <Theme>
            {props => <div className="App">{this.props.children}</div>}
           </Theme>
        )
    }

}

And then deeper in the tree :

import React from "react"
import { RaisedButton } from "material-ui"

export default class NestedComponent {

    render() {
        return (
            <div className="NestedComponent">
                <RaisedButton label="Awesome Button" />
            </div>
        )
    }

}

@shaurya947
Copy link
Contributor

See here: as of now, context is only passed through the owner-ownee relationship (not the parent-child relationship). This is to be changed in a future version of react.

I understand that the following construct:

<Theme theme={myTheme}>
    {props => // props contains the ThemeManager instance in "themeManager" property (also added to context)
      <RaisedButton label='Super Button' primary={true}/>
    }
</Theme>

is used so that context from the Theme HOC can be passed down as owner-based context instead of parent-based context. Please correct me if my understanding is wrong. However, down the chain of hierarchy, I can still see some warnings pop up (that say that parent-based and owner-based contexts differ). Is this is a known issue? If so, is there an identified work-around?

Hopefully, with React v0.14 (where contexts are passed only based on the parent-child relationship), this will be as simple as:

<Theme theme={myTheme}>
      <RaisedButton label='Super Button' primary={true}/>
</Theme>

shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 15, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 17, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 21, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 22, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 22, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
shaurya947 pushed a commit to shaurya947/material-ui that referenced this pull request Sep 22, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
yongxu pushed a commit to yongxu/material-ui that referenced this pull request Sep 24, 2015
Separated out decorator into src/styles/theme-decorator.js so that it no longer relies on another
wrapper component.
@zannager zannager added the customization: theme Higher level theming customizability. label Mar 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

customization: theme Higher level theming customizability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants