Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.