Do forms in React have bad performance? - reactjs

I'm learning how to use <form>'s in React and most examples I've seen use a combination of state and onChange to keep track of your form's inputs:
class Form extends React.Component {
handleChange(event) {
this.setState({
inputvalue: event.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label>Name</label>
<input type="text" value={this.state.inputvalue} onChange={this.handleChange.bind(this)}/>
<input type="submit" value="Submit"/>
</form>
);
}
}
However, say I have numerous <input>'s and even some <textarea>'s which might change quite often. In that case each one of them would call the onChange method each time they are updated and the component would re-render on every key press.
Seeing as how people can type pretty fast, could this be an area for concern?

In a small testing I discovered that React successfully performs a shallow compare in the state and changes in the DOM in just the components that need a re-render. In Chrome I enabled the highlights (Paint Flashing) of the areas that were repainted by React in the DOM.
See the Paint Flashing in action.
In my example note that onChange will run on every keystroke to update the React state, the displayed value will update as the user types (based on the React Docs https://reactjs.org/docs/forms.html#controlled-components).
Also you can see my code here: https://codepen.io/anon/pen/KxjJRp
class Application extends React.Component {
state = {
value1: "",
value2: "",
value3: "",
value4: ""
}
onChange = ({target: {value, name}}) => {
this.setState({
[name]: value
})
}
render() {
const { state: { value1, value2, value3, value4 } } = this
return (
<div>
<label>Value 1</label>
<input type="text" value={value1} name="value1" onChange={this.onChange}/>
<label>Value 2</label>
<input type="text" value={value2} name="value2" onChange={this.onChange}/>
<label>Value 3</label>
<input type="text" value={value3} name="value3" onChange={this.onChange}/>
<label>Value 4</label>
<input type="text" value={value4} name="value4" onChange={this.onChange}/>
</div>
)
}
}

I am not totally sure of the best way to handle this but I could see an implementation of setting an onblur method to handle updating state. onChange would be constantly updating but this might not be the worst thing as it likely will not re-render the page with every keystroke.

Related

React.js, improve generating object from form inputs

In vanilla JS I could just use formData for this, and make an Object.fromEntries() from it.
In React I wasn't sure how to do this right, so here's what I came up with after some googling around:
class App extends Component{
constructor(props){
super(props);
this.autoData = {
title:'',
description:'',
year:'',
color:'',
status:'',
price:''
}
}
handleAutoData = e => {
if (e.target.id in this.autoData) {
this.autoData[e.target.id] = e.target.value
}
}
handleAutoForm = e => {
e.preventDefault()
if (Object.keys(this.autoData).every(k => this.autoData[k])) {
this.props.addAuto(this.autoData)
}
}
render(){
...
return (
<div className="App">
<form className="form" onSubmit={this.handleAutoForm}>
<input type="text" className="form_text" placeholder="Name" id="title" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Year" id="year" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Price" id="price" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Details" id="description" onChange={this.handleAutoData} />
<button type="submit"> Send > </button>
</form>
</div>
)
}
}
This does the job and handleAutoForm pushes the object into Redux store. However:
Is there a better alternative for onChange event? I know it's the go-to way of handling form inputs, but right now it's spamming/overwriting my values on every keystroke. I'd only like to push a value once I stop typing/field loses focus. Out of alternatives, I saw articles of onFocusOut, but it's not supported or has issues.
Right now I'm mutating the component's state directly. Not sure if it's critical, since I'll be pushing the state to Redux anyway. I wanted a local object inside handleAutoData, just so I could write the values into it, but every time an onChange is called, a new object is made and it overwrites the previous values. Problem is, I can't use setState because of e.target's nature - it keeps complaining about missing brackets on render, because of all the dots (when I do something like this.setState({autoData[e.target.id]:e.target.value}) ). And if I assign it to a temporary variable (like let autoKey = e.target.id), setState pushes the autoKey as key, instead of e.target.id. What could be done with this?
Usual way is to use name or id along with onChange event.
state = {
title:'',
description:'',
year:'',
color:'',
status:'',
price:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
});
handleSubmit = (e)=>{
e.preventDefault();
//Push state to redux,make API
call etc
}

Difference between React controlled component and uncontrolled component

I am new to react and while learning I come across this example of controlled component.
function App() {
let [fName, setFName]=useState('');
return (
<div className="container">
<h1>Hello {fName }</h1>
<input name ='fname' value={fName} onChange={(e)=> setFName(e.target.value)} type="text" placeholder="What's your first name?" />
</div>
);
}
just adding value={fName} makes is controlled . I don't actually understand what does it mean by controlled component and uncontrolled. Can you explain it from beginner prospective.
An uncontrolled component means that you will let the component itself manage the value. It's own internal mechanism will keep track of it.
Now when you add the value property to the input component, you will start to "control" the component yourself. The value you put into that property, will be the value that will be displayed.
You can literally control the value yourself, by just passing it in as is, or by changing the value before passing it in.
Controlled Components
These components have what is called a callback function that is triggered each time we enter something new in the form element.
Triggering the function will typically store or update what we type in the same React component that displays the form element that was used
Most widespread use it with forms
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Unontrolled Components
These components such as <input> typically maintain their own state and update it based on user input.
In other words, they will accept what we type and have the responsibility of remembering it, and in order to retrieve the values they remembered, you have to get it when you need it.
The latter usually happens during form submission. They can be classified under uncontrolled components.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Here, Reactjs documentation provided explanation.
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a dumb component/stateless component.
An Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
React form components support both controlled and uncontrolled usage:
// Uncontrolled:
<input type="text" defaultValue="hey" ref={inputRef} />
// Controlled:
<input type="text" value={this.state.value} onChange={onHandleChange} />

onChange handler isn't updating state when I type in input

My onChange handler should be setting my state with whatever I type into the input, but when I log my state afterwards, it is not getting updated.
I am not sure what to try.
handleInputChange(e) {
this.setState({
insightsDTO: {
[e.target.id]: e.target.value
}
}, () => console.log(this.state, 'handle input change this.state'))
}
<div className="Form-group publish-insights-input">
<label class="Form-label">
URL <span className="asterisk">*</span>:
</label>
<input
type="text"
id="insightURL"
placeholder="URL"
class="Form-input"
onChange={this.handleInputChange}
value={this.state.insightsDTO ? this.state.insightsDTO["insightURL"] : ""}
/>
</div>;
When I check it out on the devtools, it's setting [e.target.id] to "insightURL" and e.target.value to whatever I'm typing in, but then afterwards when I log the state, I am not seeing that e.target.value. It's still just an empty string.
Did you bind your change handler in the constructor?
constructor(props) {
super(props);
this.state = {};
this.handleInputChange = this.handleInputChange.bind(this);
}
See a full example here:
Also see React docs for controlled components: https://reactjs.org/docs/forms.html#controlled-components
Unrelated, also make sure to use className (not class).

A single onChange listener on a <form> tag

I was just playing a bit with a more generic way of dealing with form data, and figured that setting a single onChange listener on the wrapping form tag should work for all changes to input fields.
And indeed, it does - no surprise there. Events get called, I know what changed by inspecting event.target.name and can update state accordingly. Fully as expected.
However, React doesn't seem to like it and spams the well known "You provided a value prop to a form field without an onChange handler" console warning.
Would there be any other reason for not doing this, apart from the console warning? It seems to eliminate a lot of duplication React otherwise gets criticised about.
class App extends Component {
state = {
name: 'Default Name',
number: 12,
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
})
}
render() {
const { crashLogo } = this.props;
return (
<div className="App">
<form onChange={this.handleChange}>
<input type="text" name="name" value={this.state.name} />
<input type="number" name="number" value={this.state.number} />
</form>
</div>
);
}
}
Just for clarity: I'm not asking for alternative solutions, I know I can set the same change listener directly on every input, use redux-form, own wrapper components with context magic and so on...

Best Practice For Reading Form Data in React

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
This is how I handle all fields in a form.
var My_Field=React.createClass({
_onChange: function(evt) {
var e=this.props.parent_form;
if (e) {
e.setState({email: evt.target.value});
}
},
render: function() {
return (
<input type="text" name={this.props.name} placeholder={this.props.label} onChange={this._onChange}/>
);
}
});
var My_Form=React.createClass({
getInitialState: function() {
return {
email: "",
};
},
_onSubmit: function(evt) {
evt.preventDefault();
alert(this.state.email);
},
render: function() {
return (
<form onSubmit={this._onSubmit}>
<My_Field name="email" label="Email" parent_form={this}/>
<input type="submit" value="Submit"/>
</form>
);
}
});
For a smaller project I have been using a single onChange call back for all inputs - something like this...
HandleClick(event) {
let values = this.state.values;
values[event.target.name] = event.target.value;
this.setState({values});
}
This requires that you name your inputs the same as you name their state property but I quite like that. Then you give the value stored in state to the value attribute of your input and you're all set - all your form state stored in one place with a single handler function.
Of course there are more scalable ways - I was just reading about a framework for react called formsy that looked interesting. Here's a tutorial:
http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html
Hope that helps
Dan
You can read form data using only one function.
class SignUp extends Component {
constructor(){
super();
this.handleLogin = this.handleLogin.bind(this);
}
handleLogin(e){
e.preventDefault();
const formData = {};
for(const field in this.refs){
formData[field] = this.refs[field].value;
}
console.log('-->', formData);
}
render(){
return (
<form onSubmit={this.handleLogin} className="form-horizontal">
<div className="form-group text-left">
<label className="control-label col-sm-2">Name:</label>
<div className="col-sm-10">
<input ref="name" type="text" className="form-control" name="name" placeholder="Enter name" />
</div>
</div>
<div className="form-group text-left">
<label className="control-label col-sm-2">Email:</label>
<div className="col-sm-10">
<input ref="email" type="email" className="form-control" name="email" placeholder="Enter email" />
</div>
</div>
<div className="form-group text-left">
<label className="control-label col-sm-2">Password:</label>
<div className="col-sm-10">
<input ref="password" type="password" className="form-control" name="password" placeholder="Enter password" />
</div>
</div>
<div className="form-group text-left">
<div className="col-sm-offset-2 col-sm-10">
<button type="submit" className="btn btn-primary btn-block">Signup</button>
</div>
</div>
</form>
)
} }export default SignUp;
The quickest and easiest way, using React's useState():
const Form = () => {
const [fullName, setFullName] = React.useState("");
const [is18, setOver18] = React.useState(false);
return (
<form>
<input
type="text"
name="fullName"
value={fullName}
onChange={event => setFullName(event.target.value)}
/>
<input
type="checkbox"
name="over18"
checked={is18}
onChange={() => setOver18(!is18)}
/>
</form>
);
};

Resources