Reputation: 49804
I have a search input.
const { searchMails } = this.props;
searchMails(keyword);
I added the lodash's debounce
based on this answer on Stack Overflow.
const { searchMails } = this.props;
const debounceSearchMails = debounce(searchMails, 1000);
debounceSearchMails(keyword);
The action
export const searchMails = keyword => ({ type: SEARCH_MAILS, payload: keyword });
However, after adding debounce
, when I type "hello", it will still trigger 5 times searchMails
after 1 second. The payload are
h
he
hel
hell
hello
How can I use debounce correctly? Thanks
UPDATE 1: add full codes
import React, { PureComponent } from 'react';
import { Field, reduxForm, reset } from 'redux-form';
import { Form } from 'reactstrap';
import debounce from 'lodash/debounce';
class Search extends PureComponent {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(values) {
const { searchMails } = this.props;
const debounceSearchMails = debounce(searchMails, 1000);
debounceSearchMails(values.keyword);
}
render() {
const { handleSubmit, keyword } = this.props;
return (
<Form onSubmit={handleSubmit(this.onSubmit)}>
<Field name="keyword" component="input" type="search" onChange={() => setTimeout(handleSubmit(this.onSubmit))} />
</Form>
);
}
}
function validate(values) {
const errors = {};
return errors;
}
export default reduxForm({
validate,
form: 'searchForm'
})(Search);
UPDATE 2:
I changed my action to
const searchMails0 = keyword => ({ type: SEARCH_MAILS, payload: keyword });
export const searchMails = debounce(searchMails0, 1000);
But still same.
UPDATE 3: this time I changed to this, but still same.
class Search extends PureComponent {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.debouncedSubmit = debounce(this.onSubmit, 1000);
}
onSubmit(values) {
const { searchMails } = this.props;
searchMails(values.keyword);
}
render() {
const { handleSubmit, keyword } = this.props;
return (
<Form onSubmit={handleSubmit(this.debouncedSubmit)}>
<Field name="keyword" component="input" type="search" onChange={() => setTimeout(handleSubmit(this.debouncedSubmit))} />
</Form>
);
}
}
UDPATE 4:
I found the issue is somehow related with setTimeout
, if I have that like below, debounce
won't work. If I remove setTimeout
, debounce
will work. But then onChange
will always return last value. So I do need have it because of redux-form's this "issue"
<Field component="input" type="search" onChange={() => setTimeout(handleSubmit(debounce(this.onSubmit, 1000)))}/>
Upvotes: 2
Views: 5967
Reputation: 49804
First big thank you for @zerkms. Without his guide to the right direction, I cannot make it.
You should only create the debounced function once and then use it. It is debounced function that holds internally the state necessary for debouncing. At the moment you recreate it on every keystroke. – zerkms
After checking onChange's type, this is final working code:
class Search extends PureComponent {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.debouncedOnChange = debounce(this.onChange, 1000);
}
onSubmit(values) {
const { searchMails } = this.props;
searchMails(values.keyword);
}
onChange(event, newValue, previousValue) {
const { searchMails } = this.props;
searchMails(newValue); // the second parameter is new value
}
render() {
const { keyword } = this.props;
return (
<Form onSubmit={handleSubmit(this.onSubmit)}>
<Field component="input" type="search" onChange={this.debouncedOnChange}/>
</Form>
);
}
}
Lessons learned:
I never thought I can do something like this.debouncedSubmit = debounce(this.onSubmit, 1000);
in constructor.
And I always thought I have to use handleSubmit
from redux-form, but turns out it is not for all cases.
Need go deep.
Upvotes: 7