22

Currently in react js, when I want to bind a text area or an input with a "state", I will need to set the onChange method and setState() everytime user type in a single letter

I heard if you setState react js refresh and re-render everything in this component

Is there any more efficient way to do so? using "shouldComponentUpdate" will be improper in this case since if I don't make "state" update, all user input will be stuck..

user3718395
  • 477
  • 2
  • 4
  • 9

4 Answers4

22

Well, that's how you implement controlled input elements in React.

However, if performance is a major concern of yours, you could either isolate your input element in a separate stateful component, hence only triggering a re-render on itself and not on your entire app.

So something like:

class App extends Component {    
  render() {
    return (
      <div>
        ...
        <MyInput />
        ...
      </div>
    );
  }
}


class MyInput extends Component {
  constructor() {
    super();
    this.state = {value: ""};
  }

  update = (e) => {
    this.setState({value: e.target.value});
  }

  render() {
    return (
      <input onChange={this.update} value={this.state.value} />
    );
  }
}

Alternatively, you could just use an uncontrolled input element. For example:

class App extends Component {    
  render() {
    return (
      <div>
        ...
        <input defaultValue="" />
        ...
      </div>
    );
  }
}

Though, note that controlled inputs are generally recommended.

Chris
  • 57,622
  • 19
  • 111
  • 137
  • 1
    I wonder if there is a way to overload `update` so that you can track `.val().length` and only re-render/change state when the difference between the current length and the previous length exceeds a certain number. (I'm not a react person, just a basic js person, but react is relevant to my interests). – cowbert Oct 25 '17 at 22:14
  • @cowbert, no this isn't really possible. If react is responsible for controlling the input, updating the state on every X letter typed would never work because you would never be able to type the first letter in to begin with. Simple demo: http://jsfiddle.net/1m0zrna8/ – Chris Oct 25 '17 at 22:33
  • @Chris why are the controlled inputs recommended over the uncontrolled when by default you benefit in performance with uncontrolled? – Pasha Skender Dec 07 '22 at 21:44
3

As @Chris stated, you should create another component to optimize the rerendering to only the specified component.

However, there are usecases where you need to update the parent component or dispatch an action with the value entered in your input to one of your reducers.

For example I created a SearchInput component which updates itself for every character entered in the input but only call the onChange function only if there are 3 characters at least.

Note: The clearTimeout is useful in order to call the onChange function only when the user has stopped typing for at least 200ms.

import React from 'react';

class SearchInput extends React.Component {
  constructor(props) {
    super(props);
    this.tabTimeoutId = [];
    this.state = {
      value: this.props.value,
    };

    this.onChangeSearch = this.onChangeSearch.bind(this);
  }

  componentWillUpdate() {
    // If the timoutId exists, it means a timeout is being launch
    if (this.tabTimeoutId.length > 1) {
      clearTimeout(this.tabTimeoutId[this.tabTimeoutId.length - 2]);
    }
  }

  onChangeSearch(event) {
    const { value } = event.target;

    this.setState({
      value,
    });

    const timeoutId = setTimeout(() => {
      value.length >= this.props.minSearchLength ? this.props.onChange(value) : this.props.resetSearch();
      this.tabTimeoutId = [];
    }, this.props.searchDelay);

    this.tabTimeoutId.push(timeoutId);
  }

  render() {
    const {
      onChange,
      minSearchLength,
      searchDelay,
      ...otherProps,
    } = this.props;

    return <input
      {...otherProps}
      value={this.state.value}
      onChange={event => this.onChangeSearch(event)}
    />
  }
}

SearchInput.propTypes = {
  minSearchLength: React.PropTypes.number,
  searchDelay: React.PropTypes.number,
};
SearchInput.defaultProps = {
  minSearchLength: 3,
  searchDelay: 200,
};

export default SearchInput;

Hope it helps.

yuantonito
  • 1,274
  • 8
  • 17
-1

You need to bind the onChange() event function inside constructor like as code snippets :

class MyComponent extends Component {
  constructor() {
    super();
    this.state = {value: ""};
    this.onChange = this.onChange.bind(this)
  }

  onChange= (e)=>{
    const formthis = this;
    let {name, value} = e.target;

    formthis.setState({
      [name]: value
    });
  }

  render() {
    return (
      <div>
        <form>
            <input type="text" name="name" onChange={this.onChange}  />
            <input type="text" name="email" onChange={this.onChange}  />
            <input type="text" name="phone" onChange={this.onChange}  />
            <input type="submit" name="submit" value="Submit"  />
        </form>
      </div>
    );
  }
}
Rahul Gupta
  • 991
  • 4
  • 12
-4

You don't need a complicated react solution to this problem, just a little common sense about when to update state. The best way to achieve this is to encapsulate your setState call within a timeout.

class Element extends React.Component {
    onChange = (e) => {
        clearTimeout(this.setStateTimeout)
        this.setStateTimeout = setTimeout(()=> {
            this.setState({inputValue: e.target.value})
        }, 500)
    }
}

This will only set state on your react element a 500ms after the last keystroke and will prevent hammering the element with rerenders as your user is typing.

Michael.Lumley
  • 2,345
  • 2
  • 31
  • 53
  • 2
    This will just add 500ms delay until the typed text actually shows up in the input field. It can indeed avoid constant re-rendering, but the perceived performance is severely affected, and the experience will be broken. – CookieEater Apr 11 '20 at 17:32
  • @CookieMonster I disagree - you're not incurring any cost (other than creating a timeout) every call. You're also not blocking the text from showing up in the input field, because the input isn't bound to anything. Care to explain your reasoning? – Michael.Lumley Apr 13 '20 at 11:29
  • Oh, I think I see the point you're trying to make now, but it's wrong. Maybe my example is insufficiently clear. The change handler isn't bound to the react element, it's bound to the input. The input will update immediately - because you don't bind `val={this.state.val}` on the input. Instead you just manually update the state on change. (But you do it with a 500ms delay.) – Michael.Lumley Apr 13 '20 at 11:32