Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

debounce textarea input with react/redux

I'm trying to debounce textarea value with react/redux and show the debounced value in div#preview but i'm getting synthetic event warning after first function call.

I have reducer for handling textarea value state which is working as intended, but for simplicity i've wrote local state in this snippet.

If there is a better method besides debounce to avoid react rerendering after each keypress I would love to know. Thanks in advance.

class TextArea extends React.Component {
  constructor(props){
    super(props);
    this.state = {foo: ''}
  }
  
  handleInputChange = _.debounce((e) => {
    e.persist();
    let value = e.target.value;
    this.setState({foo: value});
  }, 300);

  render() {
    return (
      <div>
       <textarea onChange={(e)=>this.handleInputChange(e)} value={this.state.foo}></textarea>
       <p id="preview">{this.state.foo}</p>
      </div>
    );
  }
}


ReactDOM.render(
  <TextArea />,
  document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
like image 235
nehel Avatar asked Apr 30 '17 17:04

nehel


1 Answers

You get this error because you try to .persist() the event inside the debounce's timeout. When the timeout invokes the callback, the synthetic event was already released. So you'll have to persist the event outside of the debounce.

However, your idea has another problem. Since the textbox is a controlled component, debouncing the updated value, would cause the textbox to render (part of) the text only after the used stopped typing.

To prevent that you need to update the state for the controlled element immediately, and debounce the update for the display state (or the redux action dispatch).

For example:

class TextArea extends React.Component {
  constructor(props){
    super(props);
    this.state = { foo: '', controlled: '' }
  }
  
  updateFoo = _.debounce((value) => { // this can also dispatch a redux action
    this.setState({foo: value});
  }, 300);
  
  handleInputChange = (e) => {
    const value = e.target.value;
    
    this.setState({
      controlled: value
    });
    
    this.updateFoo(value);
  }

  render() {
    return (
      <div>
       <textarea onChange={ this.handleInputChange }
       value={this.state.controlled} />
       <p id="preview">{this.state.foo}</p>
      </div>
    );
  }
}


ReactDOM.render(
  <TextArea />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react"></div>
like image 193
Ori Drori Avatar answered Sep 22 '22 00:09

Ori Drori