15

I was reading documentation on "onChange" and I am curious as to what I would do if my forum has multiple fields like select boxes, checkboxes, text areas and inputs? Do I just do something like:

 getInitialState: function() {
    return {textArea: 'Hello!', input: 'World', ...};
  },

to the initial state and then the same concept for handling that change of that field?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
user3379926
  • 3,855
  • 6
  • 24
  • 42

3 Answers3

65

@FakeRainBrigand 's answer is pretty cool.

I want to share one in JavaScript style (use high-order function), much shorter:

/** @jsx React.DOM */

var App = React.createClass({
  getInitialState: function () {
    return {
      username: '',
      password: ''
    }
  },
  handleChange: function (key) {
    return function (e) {
      var state = {};
      state[key] = e.target.value;
      this.setState(state);
    }.bind(this);
  },
  render: function(){
    console.log(JSON.stringify(this.getFormData(), null, 4));
    return (
      <div>
        <form>
          Username: <input 
                value={this.state.username} 
                onChange={this.handleChange('username')} />
          <br />
          Password: <input type="password"
                value={this.state.password} 
                onChange={this.handleChange('password')} />
        </form>

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

React.renderComponent(<App />, document.body);
Junle Li
  • 1,035
  • 1
  • 12
  • 18
9

Edit: In retrospect, this answer is pretty bad, use Junle Li's answer instead.


Yes you can do exactly that. When you get a lot of form components, though, it can be quite verbose to write all of the handlers and the getInitialState calls, so how about a mixin?

jsbin

Note also look up react's valueLink mixin

Let's take a look at how our view will look with an example sign in form. You can call this.getFormData() to get an object with just your form state, allowing you to store other values in state as well.

// create a mixin for our form
var formMixin = makeFormMixin([
    "username",
    "password"
]);

var App = React.createClass({
  mixins: [formMixin],
  render: function(){
    return (
      <div>
        <form>
          Username: <input 
                value={this.state.username} 
                onChange={this.handleUsernameChange} />

          Password: <input type="password"
                value={this.state.password} 
                onChange={this.handlePasswordChange} />
        </form>
      </div>
    );
  }
});

This function takes an array of field names, and sets the initial state, and provides handler functions for you. You can then choose to use these, or create your own handler functions for special cases.

function makeFormMixin(fields){
  var mixin = {
    getInitialState: function(){
      var state = {};
      fields.forEach(function(field){

        state[field] = this.props[field] || "";
      }, this);
      return state;
    },
    getFormData: function(){
      var data = {};
      fields.forEach(function(field){
        data[field] = this.state[field];
      }, this);
      console.log(data);
      return data;
    }
  };

  fields.forEach(function(field){
    var method = camelJoin(["handle", field, "change"]);
    mixin[method] = function(event){
      var update = {};
      update[field] = event.target.value;
      this.setState(update);
    }
  });

  return mixin;
}

// helper function ["Makes", "things", "camel", "case"] => "makesThingsCamelCase"
function camelJoin(parts){
  return parts.map(function(part, i){
    if (i === 0) {
      return part[0].toLowerCase() + part.slice(1);
    }
    else {
      return part[0].toUpperCase() + part.slice(1);
    }
  }).join("");
}
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • 2
    I really had to constrain myself not to upvote this answer. You're right - Junle Li's answer is much better (and hence I finally chose to not upvote yours). But you do deserve credit for pointing that out yourself. I wish there was a way to give rep for humility and honesty - both of which are amazing character qualities often not credited enough. This comment is my inadequate way of saying: Kudos. And thanks for your contribution! – Johannes Pille May 25 '16 at 19:06
  • 1
    @FakeRain mixin is no longer supported – angry kiwi Feb 16 '17 at 17:58
  • @angrykiwi see the other answer. Also mixins are still supported in createClass, and you can use them on es6 classes with the react-mixin npm package. – Brigand Feb 17 '17 at 00:44
0

Some of my friends were discussing this today. The solution proposed by @JunleLi is great but it breaks Purerender which forces a re-render of all subcomponents every time a character it typed into one of the fields as it is regenerating the function.

Here is an example of our solution. We're still not sure if we're completely happy with it but it ultimately creates the function once and caches it.

I'd love to hear any feedback.

handleChange(key) {
    if(!this.changeHandlers) this.changeHandlers = {};
    return this.changeHandlers[key] || (this.changeHandlers[key] = event => this.setState({[key]: event.target.value}));
},

render() {
    return (
        <div>
          <input type="text" value={this.state.familyName} onChange={this.handleChange("familyName")} />
          <pre>{JSON.stringify(this.state, null, 4)}</pre>
        </div>
    );
}
Matt
  • 5,315
  • 1
  • 30
  • 57
  • 1
    Also if somebody uses lodash or underscore, one can easily achieve caching by wrapping callback creator with `_.memoize`. – krzychek Jun 07 '17 at 13:41