1
\$\begingroup\$

The purpose of the code is to see if a form element would get data from internal react components and it did. It was also to see how clean I could make an autocomplete component.

The autocomplete has 4 states and I use bind excessively (is this bad?). I also added a function updateSpecificState, because I found it inconvenient that React has no method to let you change a specific property in the state.

app.js:

React.render(
    <Rendrform method="post" />,
    document.getElementById('content')
);

rendrform.js: (don't worry about the name, I confused rendr and react)

var Rendrform = React.createClass({
    onSubmit: function(event) {
        console.log('I would ajax this:', $(event.target).serialize());
        event.preventDefault();
    },
    render: function() {
        var types = [
            {title: 'blog', id: 11,},
            {title: 'comment', id: 22,},
            {title: 'todo', id: 33,},
        ];
        var mappingTo = function(option) {
            return option.id;
        };
        var mappingFrom = function(option) {
            return option.title;
        };
        return (
            <form method={this.props.method} target="index.html" onSubmit={this.onSubmit}>
                <input type="text" name="author" placeholder="Enter Author" />
                <Autocomplete options={types} mappingTo={mappingTo} mappingFrom={mappingFrom} name="type" />
                <textarea name="body"></textarea><br />
                <input type="submit" value="Post" />
            </form>
        );
    }
});

autocomplete.js:

//TODO: by default options would be an array of strings,with no mapping
//TODO: the user can define mappingTo and/or mappingFrom, which both can either be a string or a function. String would be the key
var Autocomplete = React.createClass({
    //Feels like this should be a native function...
    updateSpecificState: function(object, key, value) {
        object[key] = value;
        this.setState(object);
        console.log('Current State: ', this.state);
        console.log('Current State.value: ', this.state.value);
    },
    getInitialState: function() {
        return {
            display: 'none',
            filteredOptions: this.props.options,
            value: null,
            faceValue: '',
        };
    },
    setValue: function(option) {
        this.updateSpecificState(this.state, 'value', this.props.mappingTo(option));
        this.updateSpecificState(this.state, 'faceValue', this.props.mappingFrom(option));
    },
    /* TODO: make a toggle mixin for events or something... */
    onFocus: function() {
        this.updateSpecificState(this.state, 'display', 'block');
    },
    blurTimer: false,
    onBlur: function() {
        //otherwise the blur happens bfore the click event
        this.blurTimer = window.setTimeout(function() {
            this.updateSpecificState(this.state, 'display', 'none');
            if(!this.state.value)
                this.updateSpecificState(this.state, 'faceValue', '');
        }.bind(this), 200);
    },
    onClick: function(event) {
        this.setValue(this.props.options[event.target.dataset.index]);
        window.clearTimeout(this.blurTimer);
        this.updateSpecificState(this.state, 'display', 'none');
    },
    onChange: function(event) {
        this.updateSpecificState(this.state, 'faceValue', event.target.value);
        this.updateSpecificState(this.state, 'filteredOptions', this.props.options.filter(function(option) {
            return option.title.indexOf(this.state.faceValue) != -1;
        }.bind(this)));
        //Note: If performance is an issue, then I could return here if the above filter returns more than 1.
        var exactOptionMatches = this.props.options.filter(function(option) {
            return option.title === this.state.faceValue;
        }.bind(this));
        if(exactOptionMatches.length >= 1)
            this.setValue(exactOptionMatches[0]);
        else
            this.updateSpecificState(this.state, 'value', null);
    },
    render: function() {
        var faceValue = this.state.faceValue;
        return (
            <div>
                <input type="text" onFocus={this.onFocus} onBlur={this.onBlur} onChange={this.onChange} value={faceValue} />
                <input type="hidden" name={this.props.name} value={this.state.value} />
                <div className="autocomplete-items" style={{display: this.state.display}}>
                    {this.state.filteredOptions.map(function(option, i) {
                        return <div data-index={i} onClick={this.onClick}>{this.props.mappingFrom(option)}</div>;
                    }.bind(this))}
                </div>
            </div>
        );
    },
});
\$\endgroup\$
  • \$\begingroup\$ I could remove the filteredOptions state, but the only way I could do that was to add filtering to both the HTML and the onClick... the issue being the daata attribute that i use to reference the specific option object. (also an issue might be that it's 4:30am) \$\endgroup\$ – Jason McCarrell Mar 3 '15 at 9:39
  • \$\begingroup\$ awww no advice :( I'm new with reactJS and was hoping to get better with it. \$\endgroup\$ – Jason McCarrell Mar 6 '15 at 22:24
1
\$\begingroup\$

You don't have to bind those Array.map/filter calls explicitly. Array.map/filter take an option 'thisArg': Array.map(function () {}, this);

Or you can just make those 4 functions React Class functions instead of anonymous so they get explicitly bound to this when the ReactElement is instantiated.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.