I'm trying to do something in React that would be very simple in any other framework: I want to collect a bunch of values from a form.
Previously I did this sort of thing with a Backbone View, and it was very simple:
readFormValues: function() {
this.data.foo = this.$('[name="foo"]').val();
this.data.bar = this.$('[name="bar"]:checked').val();
});
But in React I can't seem to find an easy way to do that. It seems my only options are to ...
NOTE: Apologies for the formatting: code blocks and lists don't play well together :-(
Completely bypass React and use the jQuery + e.target
to access the form:
handleSubmit: function(e) {
var $form = $(e.target).parents('form:first');
this.data.foo = $form.find('[name="foo"]);
},
render: function() {
return <form onSubmit="handleSubmit"><input name="foo"/></form>;
}
That works, and is simple, but it feels like I'm bypassing React and using JQuery when I should just be using React.
Provide callbacks to every form control:
handleFooClick: function(e) {
this.data.foo = event.target.value;
},
render: function() {
return <form><input name="foo" onChange="handleFooChange"/></form>;
}
This appears to be the React/Flux way, but it feels like a crazy amount of unnecessary work. In my Backbone example I needed just one line per form control, but with this approach every last control I build has to have its own onChange handler (and I have to hook that handler up to every element as I render it).
EDIT: One more disadvantage of this approach is that inside the callbacks this.props
and this.state
won't point to my form control's props/state (it will point to the input's props/state). This means that not only do I have to write a handler per input AND add that callback to every input as I render, but I also have to pass in my data object to every input!
Use refs:
handleSubmit: function(e) {
this.state.data.foo = this.refs.foo.value;
},
render: function() {
return <form><input ref="foo"/></form>;
}
This seems like a more sane solution, as I only need to add a "ref" attribute to every form control, and then I can read the data as easily as I could in Backbone. However, all the React documentation suggests that using refs that way is wrong (all of the examples using refs involve sending signals to the controls, eg. "focus on this input", not on reading data out of the controls).
I feel like there must be a "React-ive" way to access my form data that isn't needlessly complex, but I'm not seeing it because I don't understand React well enough. If any React expert could explain what I'm missing I would greatly appreciate it.
First, jQuery is an unnecessary dependency and it's not the cleanest option so let's rule it out.
Next, refs have issues with flexibility. See this answer for details. Let's rule refs out for all but the simplest cases.
That leaves option #2 - what Facebook calls controlled components. Controlled components are great because they cover all use cases (like validation on keyup). Although it's not much code, if you'd rather not add a simple change handler for each form element, you might use one change handler for all elements with the use of bind
. Something like this:
handleChange: function(fieldName, e) {
console.log("field name", fieldName);
console.log("field value", e.target.value);
// Set state or use external state.
},
render: function() {
var someValue = this.state.someValue; // Or a prop is using external state
return (
<div>
<input
name="someName"
value={someValue}
onChange={this.handleChange.bind(this, "someName")} />
</div>
)
}
Or for an even cleaner way, see this answer.
You can use ReactLink
to create a two-way binding between your react component and your state.
Described here: https://facebook.github.io/react/docs/two-way-binding-helpers.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With