0

I want to build a serach bar with a "delayed" action, waiting 2 seconds before starting the search.

A normal search bar will fire a new search on every key pressed, and that is generating some delay and extra processing in the browser as I have too many data to search for.

So here is my approach:

import React, { Component } from "react";
import PropTypes from "prop-types";

const TIMER_INTERVAL = 2000;

class DelayedSearchBar extends Component {
    static propTypes = {
        onChange: PropTypes.func,
        placeHolder: PropTypes.string,
        searchText: PropTypes.string
    };

    state = {
        searchText: this.props.searchText,
        timer: null
    };

    handleChange = event => {
        // Cancel previous timer if exists
        if (this.state.timer) clearTimeout(this.state.timer);

        // Create the new timer
        let timer = null;
        if (this.props.onChange)
            timer = setTimeout(
                this.props.onChange(event.target.value),
                TIMER_INTERVAL
            );

        this.setState({
            searchText: event.target.value,
            timer: timer
        });
    };

    render() {
        return (
            <div className="uxm-search-input">
                <input
                    autoFocus={true}
                    type="text"
                    onChange={this.handleChange}
                    placeholder={this.props.placeHolder}
                    value={this.state.searchText}
                />
            </div>
        );
    }
}

export default DelayedSearchBar;

My problem is that the setTimeout is being fired once the key is pressed, seeming not to wait 2 seconds before calling onChange() as I expected.

Why is the onChange() function being fired and to fix it to respect the setTimeout delay ?

Mendes
  • 17,489
  • 35
  • 150
  • 263
  • 1
    `timer = setTimeout( this.props.onChange(event.target.value), TIMER_INTERVAL );` **calls** `this.props.onChange(event.target.value)` and passes its return value into `setTimeout`, exactly the way `bar(foo())` **calls** `foo` and passes its return value into `bar`. Instead, you want to pass a *function* into `setTimeout`. – T.J. Crowder Jan 07 '20 at 10:17
  • Side note: Since the timer handle isn't used for rendering, it shouldn't be part of your state. It can just be an instance property outside of state. – T.J. Crowder Jan 07 '20 at 10:18
  • How to fix? `setTimeout(() => this.props.onChange(event.target.value), TIMER_INTERVAL)` ? – Mendes Jan 07 '20 at 10:20
  • See the linked question's answers. (But: Yes, that would work! :-) ) – T.J. Crowder Jan 07 '20 at 10:21

0 Answers0