cssinjs / theming

Unified CSSinJS theming solution for React
300 stars 39 forks source link

Feature: theme listener #3

Closed iamstarkov closed 7 years ago

iamstarkov commented 7 years ago

as a way to create custom High-Order Components which are supposed to react to or interact with theme updates.

as an example withTheme has been refactored and became very lightweight and simple:

class withTheme extends React.Component {
  static contextTypes = themeListener.contextTypes;
  constructor(props) {
    super(props);
    this.state = { theme: {} };
    this.setTheme = theme => this.setState({ theme });

    this.themeListenerInit = themeListener.init.bind(this);
    this.themeListenerSubscribe = themeListener.subscribe.bind(this);
    this.themeListenerUnsubscribe = themeListener.unsubscribe.bind(this);
  }
  componentWillMount() {
    this.themeListenerInit(this.setTheme);
  }
  componentDidMount() {
    this.themeListenerSubscribe(this.setTheme);
  }
  componentWillUnmount() {
    this.themeListenerUnsubscribe();
  }
  render() {
    const { theme } = this.state;
    return <Component theme={theme} {...this.props} />;
  }
};
coveralls commented 7 years ago

Coverage Status

Coverage decreased (-0.3%) to 94.068% when pulling aa1cb576b7bfe084fc5e8964f1ca38a4b05dc94f on feat/theme-listener into af0faaa080d263595b74cdb9399b19e7f22a6535 on dev-review.

iamstarkov commented 7 years ago

If themeListener offers good enough level of abstraction, then i'll write tests and docs for it. then lets merge it

iamstarkov commented 7 years ago

If themeListener offers good enough level of abstraction

if it doesnt, lets iterate and think of smth

kentcdodds commented 7 years ago

Honestly, I'm not the right person to ask about the theme functionality in glamorous. I neither developed it nor use it. @vesparny was the one who implemented it, so he probably has some thoughts :)

mxstbr commented 7 years ago

I like this, seems like a good level of abstraction!

iamstarkov commented 7 years ago

@mxstbr nice

iamstarkov commented 7 years ago

@kof your turn

iamstarkov commented 7 years ago

@vesparny what do you think?

iamstarkov commented 7 years ago

@vesparny can you take a look?

kentcdodds commented 7 years ago

Maybe @kwelch would have an opinion here...

kwelch commented 7 years ago

Personally, I prefer the lowercase withTheme. One thing that was added in glamorous that I was a fan of was the merging of local theme with global theme. As it is written now if a theme prop is passed it will take precedence over the theme in context.

Here is how the merging is being done in glamorous. https://github.com/paypal/glamorous/blob/afbd1e1cf106ae46dd53b89015b46649bc833155/src/theme-provider.js#L17-L20

iamstarkov commented 7 years ago

@kwelch no, right now we merge outer theme and current one as well, i have tests for that

kwelch commented 7 years ago

Ah, thanks. I was only looking at within the scope of this PR. 😝

iamstarkov commented 7 years ago

accidentally closed this pull-request. will reopen it soon with updated implementation, tests and docs

coveralls commented 7 years ago

Coverage Status

Coverage increased (+0.8%) to 93.277% when pulling abd34dffbe4baa5f27b3fcffb36e42400cc6e262 on feat/theme-listener into 1a3e02740cbd6ddcd5bb2f0ce4112204baf3974e on dev-review.

kof commented 7 years ago

lgtm

coveralls commented 7 years ago

Coverage Status

Coverage increased (+1.2%) to 93.636% when pulling 2a9e33f682eaf9aaf248f2808c2ec0224b7fdf5e on feat/theme-listener into df0a20efeaf812e72a3c6265bbf6c4eb3ab9db01 on master.

kwelch commented 7 years ago

I put in a few comments, mostly questions.

coveralls commented 7 years ago

Coverage Status

Coverage increased (+0.5%) to 92.92% when pulling a536d3017bf70f1951d40e7e9c032131fa52c964 on feat/theme-listener into df0a20efeaf812e72a3c6265bbf6c4eb3ab9db01 on master.

coveralls commented 7 years ago

Coverage Status

Coverage increased (+1.2%) to 93.636% when pulling 69836a0180f8d61c7319fb29035f0f84d867ba33 on feat/theme-listener into 3cd9d5b1fd403dc97fdc00b16c518abddd40d016 on master.

iamstarkov commented 7 years ago

this branch has been published under @iamstarkov/theming-w-listener@1.0.1 if one want to try out themeListener integration

iamstarkov commented 7 years ago

i started to integrate it into jss, and it seems to be working. But themeListener needs to be adjusted a bit, but its a good news, because API surface will be smaller and simpler

iamstarkov commented 7 years ago

status update:

iamstarkov commented 7 years ago

no more bindings:

import { themeListener } from 'theming';

function CustomWithTheme(Component) {
  return class CustomWithTheme extends React.Component {
    static contextTypes = themeListener.contextTypes;
    constructor(props) {
      super(props);
      this.state = { theme: {} };
      this.setTheme = theme => this.setState({ theme });
    }
    componentWillMount() {
      this.setTheme(themeListener.initial(this.context))
    }
    componentDidMount() {
      this.unsubscribe = themeListener.subscribe(this.context, this.setTheme);
    }
    componentWillUnmount() {
      this.unsubscribe();
    }
    render() {
      const { theme } = this.state;
      return <Component theme={theme} {...this.props} />;
    }
  }
}
iamstarkov commented 7 years ago

published as @iamstarkov/theming-w-listener@1.0.2 if one want to try it out

jcheroske commented 7 years ago

What is the preferred method to map from the theme to props? For instance, if I wanted to pull stuff out of the theme and hand it to a MUI component, what would that look like? I'd like to see mapping from theme to props be a first-class operation. Something like:

import {mapTheme} from 'theming'

const mapThemeToProps = theme => ({
  underlineStyle: {
    color: theme.brandColors.color3
  }
})

const enhancer = mapTheme(mapThemeToProps)

export default enhancer(MyComponent)

Knowing that this lib will be embedded in react-jss, what I'd really like to see is the theme->props mapping functionality be elevated to a peer of the theme->jss-styles functionality. Not sure if injectSheet already takes in a second argument, but I've done something like this and it worked well:

import injectSheet from 'react-jss'

const mapThemeToStyles = theme => ({
  button: {
    color: theme.buttons.color
  }
}

const mapThemeToProps = theme => ({
  zDepth: theme.buttons.zDepth
})

const enhancer = injectSheet(mapThemeToStyles, mapThemeToProps)

const MyButton = ...

export default enhancer(MyButton)

Maybe this already exists in this PR?

iamstarkov commented 7 years ago

i tried this minimalistic abstraction in react-jss and it fits quite nicely

coveralls commented 7 years ago

Coverage Status

Coverage increased (+0.9%) to 93.578% when pulling d0ccc43b5d55d719fbaa7346c202cc587097f68a on feat/theme-listener into 8ad0df26ed29ef891dad1d3fadb6e36cd6e66110 on master.