最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use debounce with redux-form correctly? - Stack Overflow

programmeradmin0浏览0评论

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" ponent="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" ponent="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 ponent="input" type="search" onChange={() => setTimeout(handleSubmit(debounce(this.onSubmit, 1000)))}/>

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" ponent="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" ponent="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 ponent="input" type="search" onChange={() => setTimeout(handleSubmit(debounce(this.onSubmit, 1000)))}/>
Share Improve this question edited Oct 29, 2017 at 9:01 Hongbo Miao asked Oct 29, 2017 at 7:05 Hongbo MiaoHongbo Miao 50k67 gold badges198 silver badges327 bronze badges 23
  • 1 Show your ponent and an event handler. – zerkms Commented Oct 29, 2017 at 7:09
  • @zerkms done! just add – Hongbo Miao Commented Oct 29, 2017 at 7:14
  • 2 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 Commented Oct 29, 2017 at 7:15
  • @zerkms oh, thanks. check my new update. I changed my action, but still same. Maybe my usage is still wrong. Would you mind giving some suggestions? – Hongbo Miao Commented Oct 29, 2017 at 7:27
  • 1 @zerkms I tried onChange={() => handleSubmit(debounce(this.onSubmit, 1000))}, which works perfect for debounce, but then without setTimeout, I cannot get latest value in onChange. I have to find a way making both debounce work and also get latest value in onChange. – Hongbo Miao Commented Oct 29, 2017 at 8:59
 |  Show 18 more ments

1 Answer 1

Reset to default 7

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 ponent="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.

发布评论

评论列表(0)

  1. 暂无评论