JSFiddle - React, Tailwind, and code Playground

by Dustin Getz

HTML

<script src="http://fb.me/react-with-addons-0.9.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.9.0.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore.js"></script>
<script src="http://fb.me/react-js-fiddle-integration.js"></script>

JavaScript 1.7

/** @jsx React.DOM */

var App = React.createClass({
    getInitialState: function () {
        return {
            count: 0
        };
    },
    render: function() {
        var cursor = Cursor.build(this.state, this.setState.bind(this), cloneDeep);
        return this.transferPropsTo(<Clicker cursor={cursor}/>);
    }
});

var Clicker = React.createClass({
    render: function () {
        return (
            <div>
                <span>{this.props.cursor.refine('count').value}</span>
                <button onClick={this.inc2}>+2</button>
            </div>
        );
    },
    
    inc2: function () {
        var countCursor = this.props.cursor.refine('count');
        countCursor = countCursor.onChange(countCursor.value + 1);
        countCursor = countCursor.onChange(countCursor.value + 1);
    }
});















// this constructor is private
    function Cursor(state, path, commit, clone) {
        state = clone(state); // defensive clone right away so we can't close over a stale state reference
        this.value = getRefAtPath(state, path);

        this.onChange = function (nextValue) {
            var nextState;
            nextValue = clone(nextValue); // because the call site might retain the reference and mutate

            if (path.length > 0) {
                nextState = state;
                var scoped = getRefAtPath(nextState, initial(path));
                scoped[last(path)] = nextValue;
            }
            else if (path.length === 0) {
                nextState = nextValue;
            }
            commit(nextState);
            return new Cursor(nextState, path, commit, clone);
        };

        this.refine = function (/* one or more paths through the tree */) {
            var nextPath = [].concat(path, flatten(arguments));
            return new Cursor(state, nextPath, commit, clone);
        };
    }


    /**
     * Example usages:
     * Cursor.build(this.state, this.setState.bind(this), _.cloneDeep);
     *...