Skip to content

Instantly share code, notes, and snippets.

@kristianmandrup
Last active March 31, 2016 14:28
Show Gist options
  • Save kristianmandrup/5a866aa736118bdac54fb6ad200e5b25 to your computer and use it in GitHub Desktop.
Save kristianmandrup/5a866aa736118bdac54fb6ad200e5b25 to your computer and use it in GitHub Desktop.
Reactive Component Styles for React
export default Styles {
    constructor(props, state) {
        this.props = props;
        this.state = state;
        this.createGeneric()
    }

    generic() {
        return [];
    }

    compute(styles) {
        computeStyles(styles);
        computeStyles(this.genericStyles);
    }

    computeStyles(styleObj) {
        // ignore static styles
        
        // should compare pointer of prev state (assume immutable)
        for (let [k, v] of styleObj.state) {
            // check state/props dependency and only call if either one changed
            styleObj[k] = v({state: this.state});
        }

        // should compare pointer of prev props (assume immutable)
        for (let [k, v] of styleObj.props) {
            // check state/props dependency and only call if either one changed
            styleObj[k] = v({props: this.props});
        }
        for (let [k, v] of styleObj.any) {
            // check state/props dependency and only call if either one changed
            styleObj[k] = v({state: this.state, props: this.props});
        }

        return styleObj;
    }

    createGeneric() {
        this.genericStyles = () => {
            return this.generic.reduce((prev, style) => {
                prev[style] = (x) => { return this[style](x};
            });
        }
    }

    mixin(target, ...styles) {
        return _.merge(target, styles);
    }
}

The Styles class of MyComponentStyles

import GlobalStyles from '../GlobalStyles.js'

// styles object allows "mixins"
export const styles = {
    // return style object
    // dep: state change
    // State dependency decorator, will register as state dependent
    // actually just a computed property! See aurelia
    @computed(state)
    title: (x) => {
        return {
            color: x.state.todo.completed ? 'red' : 'green';
        };
    }
}.mixin(GlobalStyles.headers);


export default class MyComponentStyles extends Styles
    constructor(props, state) {
        super(props, state)
    }

    // declare generic styles
    generic() {
        return ['header', 'footer'];
    }

    // we should be smart and only compute when necessary
    // - static
    // - props dep
    // - state dep
    // - state + props dep
    compute() {
        super.compute({
            header: subHeader(),
            title: bigTitle(),
        });
    }
}
import Styles from './styles.js';

<!-- props are global (or higher level state)
state is local state (ie. local styling)
to calculate local style state, use global and local state 
to compute new style object (one level deep only!)
 -->

export default class MyComponent extends Component {
    constructor(props) {
        super(props); // sets this.props ?
        this.createState();
    }

    createState() {
        // also use whatever props you like
        this.state = {
          x: 2
          y: 3
        };
    }

    updateStyles(nextProps, nextState) {
        this.state.styles = new Styles(nextProps, nextState).compute();
    }

    // just before component is rendered after a state update, 
    // we re-compute styles based on state
    componentWillUpdate(nextProps, nextState) {
        this.updateStyles(nextProps, nextState);
    }

    render() {
        return (
          // perhaps use a JSX pre-processor:
          // <header styles={$header, $big} => styles={[this.state.styles.header, this.state.styles.big] }

        return (
          <header styles={this.state.styles.header} >
            <title> styles={this.state.styles.title} />
            ...
          </header>
        );
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment