Krzysztof Żuraw

Debouncing forms in React with Redux - part two

  • #javascript
  • #react
  • #redux
  • #debounce

Hello! Today I continue with building debounce input in React!. Let’s get started.

What is debounce

As you saw in a previous blog post my handleChange event is firing up every time I type the letter. I don’t want that. I want it to be called when a user stops typing. One way of doing this will be using debounce.

Debounce is limiting a rate which given function will be called. Thanks to that I can tell my app to run handleChange every 250ms. It is very useful when we have event handlers that are attached to the e.g scroll of change events.

Debounce in react

I will be using lodash.debounce as it is widely used and battle-tested library.

My App component will look like this after a change:

class App extends Component {
  constructor() {
    this.state = { typedWords: [] };

    this.emitChangeDebounced = debounce(this.emitChange, 250);

  componentWillUnmount() {

  handleChange = (event) => {

  emitChange = (value) => {
    if (value !== '') {
      let typedWords = [...this.state.typedWords, value];
      this.setState({ typedWords });
  // render method here

Let’s start here with handleChange. Right now it calls emitChangeDebounced. This emit is debounced function that lodash will fire every 250ms after the user changes the input. My main logic lays inside emitChange where I set my state based on a value from the event. You may ask why do you pass instead of the whole event?

It is because of how React works. In React all events are wrapped into SyntheticEvent. This event is reused by all events inside react. To let garbage collector take it after debounce has ended I have to either provide only value to my function or call event.persist() to have my event persisted.

With event.persist() my handleChange event will look like this:

handleChange = (event) => {

If I wanted to pass entire event without persist I will get an error:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `type` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().

Testing debounce

Ok, I have my component working but how to test it? This is one of the solutions - in my App.test.js I have the following test:

it('should set state when input has changed', () => {
  const wrapper = mount(<App />);
  const searchInputWrapper = wrapper.find('#search');
  searchInputWrapper.simulate('change', {
    target: { value: 'Fake Name' },

  setTimeout(() => {
    expect(wrapper.state().typedWords).toEqual(['Fake Name']);
  }, 200);

The first few lines are component setup using enzyme. Right after that, I simulate change event on my search input. Then I use setTimeout to wait with the assertion - I will be executed when debounce stops.

That’s all for today! Thank you for reading this and thanks to BTM from reactfilux channel for help!

Github repo can be found here.