user3718395
user3718395

Reputation: 487

avoid constant re-render from "input" or "textarea" in react js

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..

Upvotes: 22

Views: 33221

Answers (4)

Michael.Lumley
Michael.Lumley

Reputation: 2385

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.

Upvotes: -4

Rahul Gupta
Rahul Gupta

Reputation: 1041

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>
    );
  }
}

Upvotes: -1

Chris
Chris

Reputation: 59551

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.

Upvotes: 22

yuantonito
yuantonito

Reputation: 1282

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.

Upvotes: 3

Related Questions