Source:  Twitter logo

I have a form with multiple text inputs. I have them all set up as controlled inputs. When typing, there is a lag of up to several seconds for the new text to display in the field. Here is an example field:

<label>Event Name</label>
<input type="text" 
       placeholder="Title"
       className="form-control"
       name="title"
       value={this.state.event.title}
       onChange={this.handleChange} />

I can't figure out what's causing it to be so slow or what to do to fix it.

UPDATED: Here's the component, should be enough to show what's going on.

let CreateEventForm = React.createClass({
  submit: function () {},
  handleChange: function(e){
    let value = e.target.value;
    let name = e.target.name;
    if(value === 'true'){
      value = true;
    }
    if(value === 'false'){
      value = false;
    }
    // If true/false toggle old
    let oldState = this.state.event[name];
    if(typeof oldState === 'boolean'){
      value = !oldState;
    }
    // If is array
    if(name.indexOf('[]') > -1){
      name = name.replace('[]', '');
      oldState = this.state.event[name];
      var pos = oldState.indexOf(value);
      if(pos > -1){
        oldState.splice(pos, 1);
      } else {
        oldState.push(value);
      }
      value = oldState;
    }
    let event = this.state.event;
    event[name] = value;
    this.setState({event: event});
    console.log(this.state.event);
  },
  getClasses(field, additionalClasses = []) {
    // If a string is passed for additional class, make array
    if(!Array.isArray(additionalClasses)){
      additionalClasses = [additionalClasses];
    }
    let useDefaultColumns = additionalClasses.filter(function(className){
        return className.indexOf('col-') > -1;
      }).length === 0;
    let hasError = function(){
      let fields = Array.isArray(field) ? field : [field];
      return fields.filter(function(field){
          return !this.props.isValid(field);
        }.bind(this)).length > 0;
    }.bind(this)();
    return classnames({
      'col-sm-4': useDefaultColumns,
      'form-group': true,
      'has-error': hasError
    }, additionalClasses);
  },
  render: function () {
    return (
      <form ref="eventForm" onSubmit={this.submit}>
        <SavedModal isOpen={this.state.saved} reset={this.resetForm} style={this.state.modals.styles} />
        <h3>Info</h3>

        <div className="row">
          <div className={this.getClasses('title')}>
            <label>Event Name</label>
            <input type="text" placeholder="Title"
                   className="form-control"
                   name="title"
                   value={this.state.event.title}
                   onChange={this.handleChange} />
            {this.renderHelpText(this.props.getValidationMessages('title'))}
          </div>
        </div>
        <div className="row">
          <div className={this.getClasses('type')}>
            <label>Event Type</label>
            <select name="type"
                    className="form-control"
                    value={this.state.event.type}
                    onChange={this.handleChange}
                    onBlur={this.props.handleValidation('type')}>
              <option value="">Select Event Type&hellip;</option>
              {this.state.calendarTypes.map(function (type, key) {
                return <option value={type.name} key={key}>{type.name}</option>
              })}
            </select>
            {this.renderHelpText(this.props.getValidationMessages('type'))}
          </div>
        </div>

        <h3>Duration</h3>

        <div className="row">
          <div className="form-group col-sm-2">
            <input type="checkbox" name="allDay" checked={this.state.event.allDay} onChange={this.handleChange}/> All Day
          </div>
        </div>
        <div className="row">
          <div className="form-group col-sm-2">
            <input type="checkbox" name="repeats" checked={this.state.event.repeats} onChange={this.handleChange}/> Repeats&hellip;
          </div>
          <br/><br/>
        </div>

        <h3>Location</h3>
        <div className="row">
          <div className={this.getClasses('location')}>
            <select name="location"
                    className="form-control"
                    value={this.state.event.location}
                    onBlur={this.props.handleValidation('location')}
                    onChange={this.handleChange}>
              <option value="">Select a Location&hellip;</option>
              {this.state.locations.map(function (location, key) {
                return (
                  <option value={location.name} key={key}>{location.name}</option>
                );
              })}
            </select>
            {this.renderHelpText(this.props.getValidationMessages('location'))}
          </div>
        </div>

        <h3>Description</h3>
        <div className="row">
          <div className={this.getClasses('description')}>
            <label>Write a description:</label>
            <textarea className="form-control"
                      name="description"
                      value={this.state.event.description}
                      onChange={this.handleChange}
                      onBlur={this.props.handleValidation('description')}
                      rows="10"></textarea>
            {this.renderHelpText(this.props.getValidationMessages('description'))}
          </div>
        </div>

        <h3>Event Details</h3>
        <div className="row">
          <div className={this.getClasses('fee')}>
            <label>Fee:</label>
            <input type="text"
                   className="form-control"
                   name="fee"
                   value={this.state.event.fee}
                   onChange={this.handleChange}
                   onBlur={this.props.handleValidation('fee')}/>
            {this.renderHelpText(this.props.getValidationMessages('fee'))}
          </div>
        </div>

        <div className="row">
          <div className="col-sm-12">
            <button className="btn btn-primary" type="submit">
              Create Event
            </button>
          </div>
        </div>

      </form>
    );
  }
});

I had a similar situation and my solution was to disable React Dev Tools. They were affecting input fields somehow. The problem is it's not enough to refresh a page if you have clicked React Dev Tools tab. They are still affecting your inputs. You have to open new page to stop them. You can also remove them from Chrome completely but I don't recommend doing this 'cause they are useful. :)

25 users liked answer #0dislike answer #025
Nek profile pic
Nek

There are many possible reasons for this to happen. I faced the similar issue and filtered the main cause to:

  • Large State, so it takes up sometime
  • React Dev Tool / Use of non minified react
  • Mutating state data

Whatever may be the reason, I found a quick fix for this. If you just want to store it to the state but not using it for live rendering. Then you can safely replace the 'onChange' to 'onBlur'. This has no dealay and lag. If you know any other case where this will not work, do let me know!

Change the code as follow:

<label>Event Name</label>
<input 
   type="text" 
   placeholder="Title"
   className="form-control"
   name="title"
   value={this.state.event.title}
   onBlur={this.handleChange} />
16 users liked answer #1dislike answer #116
aks profile pic
aks

Here is an example for an input pattern, Plug in or pattern for dealing with large forms in React?. The main thing is to have your input as a component that passes changes to the parent but doesn't update from the props if they are the same.

3 users liked answer #2dislike answer #23
J. Mark Stevens profile pic
J. Mark Stevens

My problem was that my state object was complex and thus causing rendering issues. My solution was to manage the state of the notes in my component, then on blur update the container state.

const { data, onEdit } = props;
const { containerNotes } = data;

const [notes, setNotes] = useState('');

useEffect(
  () => {
    setNotes(containerNotes);
  },
  [containerNotes],
);

const onChangeNotes = () => ({ target: { value } }) => {
  setNotes(value);
};

const onBlurNotes = (prop) => ({ target: { value } }) => {
  const newData = update(data, {
    [prop]: { $set:  value },
  });
  onEdit(newData);
};

return (
  <input 
     type="text" 
     placeholder="Title"
     name="title"
     value={notes}
     onChange={onChangeNotes()}
     onBlur={onBlurNotes('containerNotes')}
 />
)
3 users liked answer #3dislike answer #33
MCR profile pic
MCR

Here is a fix I found. It sets the parent state in onBlur. Please vet this

import React, { useState } from 'react';
import MUIField from '@material-ui/core/TextField';
import _ from 'lodash';

export default (props) => {
  const [value, setValue] = useState(props.defaultValue);
  const prop = _.omit(props, ['onChange', 'value', 'defaultValue']);

  return (
      <MUIField {...prop} value={value}
                onChange={e => { setValue(e.target.value); }}
                onBlur={() => {
                  props.onChange({ target:{ value }});
              }}/>);
};

3 users liked answer #4dislike answer #43
kir imi profile pic
kir imi

Seeing as this still gets responses, thought I'd update this and close it. Calling handleChange within onChange would cause the entire form to rerender on every change, thereby slowing it down. If you had a simple form you could just update onBlur instead or create individual input components so the rerender would be isolated to that one input. Personally, I use and would recommend using Formik for building forms as it abstracts this complexity for you.

3 users liked answer #5dislike answer #53
mrberggg profile pic
mrberggg

Apparently React-Hook-Form is great at minimising re-renders, and probably optimise performance. I have yet to try it though.

3 users liked answer #6dislike answer #63
Jammar profile pic
Jammar

I've had redux logger middleware take enough time to create this input lag. So, try disabling that as well.

0 users liked answer #7dislike answer #70
Sava B. profile pic
Sava B.

useRef instead, it doesn't re-render

const App = () => {
  const textRef = useRef();
  const showRefContent = () => {
    console.log(textRef.current.value);
  };
  return (
    <div className="App">
      <TextField inputRef={textRef} />
      <button onClick={showRefContent}>Click</button>
    </div>
  );
}
0 users liked answer #8dislike answer #80
Mohammed Mudassir profile pic
Mohammed Mudassir

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).