Skip to content

Instantly share code, notes, and snippets.

@dwighthouse
Created February 2, 2016 23:58
Show Gist options
  • Save dwighthouse/134fc2ddfe27c7322e84 to your computer and use it in GitHub Desktop.
Save dwighthouse/134fc2ddfe27c7322e84 to your computer and use it in GitHub Desktop.

App.js

'use strict';

const React = require('react');

const Store = require('./Store');
const Thing = require('./Thing');

const App = React.createClass({
    onTrigger() {
        this.setState({
            trigger: 1,
        });
    },
    componentWillMount() {
        Store.onChange(this.onTrigger);
    },
    componentWillUnmount() {
        Store.offChange(this.onTrigger);
    },
    render() {
        const thing = Store.getAt('base');
        return (
            <div>
                <Thing dataThing={thing.dataThing} moreDataWithKids={thing.moreDataWithKids} path={'base'} />
            </div>
        );
    },
});

// Note: there's usually another level of seperation here, so that the outer-most entry point is only responsible for injecting itself into the page and retrieving server-generated data from the page

const ReactDOM = require('react-dom');

ReactDOM.render(<App />, document.getElementById('ContentTarget'));

Store.js

'use strict';

const im = require('seamless-immutable');
const _ = require('lodash');

const DefaultThing = {
    dataThing: false,
    moreDataWithKids: {
        four: 'score',
        and: 7,
        years: 'ago',
    },
};

// The overarching data store for the entire app. All "mutable" data should be here, even stuff not related to the business problem, like UI state values (under an underscored key if necessary)
// This store is immutable, so take that into account in reading it and its modifying helper functions below
let store = im({
    base: newThing(),
});

// The entire pub/sub system, and the update function
const listeners = [];
function update() {
    _.each(listeners, listener => listener());
}

exports.onChange = callback => {
    const index = _.indexOf(listeners, callback);
    if (index === -1)
    {
        listeners.push(callback);
    }
};

exports.offChange = callback => {
    const index = _.indexOf(listeners, callback);
    if (index !== -1)
    {
        listeners.splice(index, 1);
    }
};

exports.getAt = (path) => _.get(store, path, void 0);

exports.store = () => store;

function setAt(path, value) {
    // If one wanted an undo function or the ability to replay state at a later time, one would push into an array, rather than overwriting the reference here
    store = store.setIn(_.toPath(path), value);
    update();
}

exports.setAt = setAt;

// How to push with an immutable array
exports.pushAt = (path, value) => {
    setAt(`${path}[${_.get(store, path, []).length}]`, value);
};

// How to remove a value in an immutable array
exports.removeAt = (path, index) => {
    const originalArray = _.get(store, path, []);
    setAt(path, originalArray.slice(0, index).concat(originalArray.slice(index + 1)));
};

function newThing() {
    return _.cloneDeep(DefaultThing);
}

exports.newThing = newThing;

// There would be additional, helper functions here, as well as context-relative data (like choice lists and additional default objects for construction)

Thing.js

'use strict';

const React = require('react');
const _ = require('lodash');

const Store = require('./Store');

const Thing = React.createClass({
    shouldComponentUpdate(nextProps) {
        // We can use reference equality here because we are using an immutable data structure
        return nextProps.dataThing !== this.props.dataThing || nextProps.moreDataWithKids !== this.props.moreDataWithKids || nextProps.path !== this.props.path;
    },
    onToggleDataThing() {
        Store.setAt(`${this.props.path}.dataThing`, !this.props.dataThing);
    },
    render() {
        return (
            <div>
                {this.props.dataThing ? (
                    <div>It's all TRUE!</div>
                ) : (
                    <div>False, very very false.</div>
                )}

                <button onClick={this.onToggleDataThing}>Swap!</button>

                <pre>
                    {JSON.stringify(this.props.moreDataWithKids, null, 4)}
                </pre>
            </div>
        );
    }
});

module.exports = Thing;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment