Skip to content

Instantly share code, notes, and snippets.

@laytong
Last active December 18, 2021 06:16
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save laytong/e2aeecf32283c3a1ab6edf8e38a78903 to your computer and use it in GitHub Desktop.
Save laytong/e2aeecf32283c3a1ab6edf8e38a78903 to your computer and use it in GitHub Desktop.
How to debounce your inputs for super fast react/redux components
import React, {Component, PropTypes} from 'react';
class BadInputComponent extends Component {
static propTypes = {
text = PropTypes.string.isRequired,
updateText = PropTypes.func.isRequired,
};
render() {
return (
<input onChange={this.handleTextChange} value={this.state.text} />
);
}
handleTextChange = (e) => {
this.props.updateText(e.target.value)
};
}
export default BadInputComponent;
import React, {Component, PropTypes} from 'react';
import debounce from 'lodash/debounce';
class DebouncedInputComponent extends Component {
static propTypes = {
text = PropTypes.string.isRequired,
updateText = PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
text: '',
};
}
componentDidMount() {
this.sendTextChange = debounce(this.sendTextChange, 500);
this.setState({text:this.props.text});
}
render() {
return (
<input onChange={this.handleTextChange} value={this.state.text} />
);
}
handleTextChange = (e) => {
this.setState({text: e.target.value});
this.sendTextChange(e.target.value.trim())
};
sendTextChange = (text) => {
this.props.updateText(text);
};
}
export default DebouncedInputComponent;
@ConAntonakos
Copy link

This is great! Thank you. I'd love to figure out a composable way of extending this functionality to a component; maybe as an HOC or through a render prop.

@Morgantheplant
Copy link

looks like this would throw if the component unmounted mid debounce. I've used

handleTextChange = ({target}) => {
    const {value} = target;
    this.setState({text: value});
    clearTimeout(this.timer);
    this.timer = setTimeout(()=>{
        this.sendTextChange(value.trim())
    }, 500);        
}

componentWillUnmount(){
    clearTimeout(this.timer);
}

@jamesr2323
Copy link

jamesr2323 commented Apr 5, 2019

Thanks for this - I've used it to fix a painful performance issue with our app. I turned this into a HOC, so it can be used like this. It should work for any input that takes value and onChange.

import TextField from '@material-ui/core/TextField'
import debouncedInput from './debouncedInput'
const  DebouncedTextField = debouncedInput(TextField, { timeout: 200 })
import React, { Component } from 'react'
import debounce from 'lodash/debounce'

export default function debouncedInput(WrappedComponent, config = { timeout: 500 }) {
  return class DebouncedTextField extends Component {
    constructor(props) {
      super(props)
      this.state = {
        value: this.props.value,
      }
      this.sendTextChange = debounce(this.sendTextChange, config.timeout)
    }

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

    sendTextChange = (e) => {
      this.props.onChange(e);
    }

    render() {
      return (
        <WrappedComponent {...this.props} value={this.state.value} onChange={this.handleTextChange.bind(this)} />
      )
    }
  }
}

@szabi84
Copy link

szabi84 commented Oct 12, 2020

Thanks for this - I've used it to fix a painful performance issue with our app. I turned this into a HOC, so it can be used like this. It should work for any input that takes value and onChange.

import TextField from '@material-ui/core/TextField'
import debouncedInput from './debouncedInput'
const  DebouncedTextField = debouncedInput(TextField, { timeout: 200 })
import React, { Component } from 'react'
import debounce from 'lodash/debounce'

export default function debouncedInput(WrappedComponent, config = { timeout: 500 }) {
  return class DebouncedTextField extends Component {
    constructor(props) {
      super(props)
      this.state = {
        value: this.props.value,
      }
      this.sendTextChange = debounce(this.sendTextChange, config.timeout)
    }

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

    sendTextChange = (e) => {
      this.props.onChange(e);
    }

    render() {
      return (
        <WrappedComponent {...this.props} value={this.state.value} onChange={this.handleTextChange.bind(this)} />
      )
    }
  }
}

Awesome, Thanks

@hansena
Copy link

hansena commented Dec 7, 2020

Same but w/ hooks...

import React, { useState } from "react";
import debounce from "lodash/debounce";

export const debounceTextbox = (Component, timeout = 500) => ({ onChange, value, ...props }) => {
	const [debouncedValue, setValue] = useState(value);
	const handleTextChange = (e) => {
		setValue(e.target.value);
		sendChange(e);
	};
	const sendTextChange = debounce((newValue) => onChange(newValue), timeout);

	return <Component {...props} onChange={handleTextChange} value={debouncedValue} />;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment