如何在React.js中执行debounce ?

我想撤消handleOnChange。

我尝试debounce(这。handleOnChange, 200)但它不起作用。

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    // make ajax call
  }
});

当前回答

如果你只需要在一个按钮中执行一个请求数据的debounce,提供的代码可能对你有帮助:

创建一个函数,以防止在请求为真或假时使用默认的条件语句 实现useState钩子和useEffect钩子

const PageOne = () => {
 const [requesting, setRequesting] = useState(false);

  useEffect(() => {
    return () => {
      setRequesting(false);
    };
  }, [requesting]);

  const onDebounce = (e) => {
    if (requesting === true) {
      e.preventDefault();
    }
    // ACTIONS
    setLoading(true);
  };

 return (
  <div>
    
    <button onClick={onDebounce}>Requesting data</button>
  </div>
 )
}

其他回答

我发现Justin Tulk的这篇文章很有帮助。在经过几次尝试后,人们会认为这是react/redux更正式的方式,结果显示它失败了,因为react的合成事件池。然后,他的解决方案使用一些内部状态来跟踪在输入中更改/输入的值,在setState之后使用一个回调,调用一个throttled/ deboundredux动作,实时显示一些结果。

import React, {Component} from 'react'
import TextField from 'material-ui/TextField'
import { debounce } from 'lodash'

class TableSearch extends Component {

  constructor(props){
    super(props)

    this.state = {
        value: props.value
    }

    this.changeSearch = debounce(this.props.changeSearch, 250)
  }

  handleChange = (e) => {
    const val = e.target.value

    this.setState({ value: val }, () => {
      this.changeSearch(val)
    })
  }

  render() {

    return (
        <TextField
            className = {styles.field}
            onChange = {this.handleChange}
            value = {this.props.value}
        />
    )
  }
}

只是最近react和lodash的另一个变体。

class Filter extends Component {
  static propTypes = {
    text: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired
  }

  state = {
    initialText: '',
    text: ''
  }

  constructor (props) {
    super(props)

    this.setText = this.setText.bind(this)
    this.onChange = _.fp.debounce(500)(this.onChange.bind(this))
  }

  static getDerivedStateFromProps (nextProps, prevState) {
    const { text } = nextProps

    if (text !== prevState.initialText) {
      return { initialText: text, text }
    }

    return null
  }

  setText (text) {
    this.setState({ text })
    this.onChange(text)
  }

  onChange (text) {
    this.props.onChange(text)
  }

  render () {
    return (<input value={this.state.text} onChange={(event) => this.setText(event.target.value)} />)
  }
}
/**
 * Returns a function with the same signature of input `callback` (but without an output) that if called, smartly
 * executes the `callback` in a debounced way.<br>
 * There is no `delay` (to execute the `callback`) in the self-delayed tries (try = calling debounced callback). It
 * will defer **only** subsequent tries (that are earlier than a minimum timeout (`delay` ms) after the latest
 * execution). It also **cancels stale tries** (that have been obsoleted because of creation of newer tries during the
 * same timeout).<br>
 * The timeout won't be expanded! So **the subsequent execution won't be deferred more than `delay`**, at all.
 * @param {Function} callback
 * @param {number} [delay=167] Defaults to `167` that is equal to "10 frames at 60 Hz" (`10 * (1000 / 60) ~= 167 ms`)
 * @return {Function}
 */
export function smartDebounce (callback, delay = 167) {
  let minNextExecTime = 0
  let timeoutId

  function debounced (...args) {
    const now = new Date().getTime()
    if (now > minNextExecTime) { // execute immediately
      minNextExecTime = now + delay // there would be at least `delay` ms between ...
      callback.apply(this, args) // ... two consecutive executions
      return
    }
    // schedule the execution:
    clearTimeout(timeoutId) // unset possible previous scheduling
    timeoutId = setTimeout( // set new scheduling
      () => {
        minNextExecTime = now + delay // there would be at least `delay` ms between ...
        callback.apply(this, args) // ... two consecutive executions
      },
      minNextExecTime - now, // 0 <= timeout <= `delay` ... (`minNextExecTime` <= `now` + `delay`)
    )
  }

  debounced.clear = clearTimeout.bind(null, timeoutId)

  return debounced
}
/**
 * Like React's `useCallback`, but will {@link smartDebounce smartly debounce} future executions.
 * @param {Function} callback
 * @param {[]} deps
 * @param {number} [delay=167] - Defaults to `167` that is equal to "10 frames at 60 Hz" (`10 * (1000 / 60) ~= 167 ms`)
 */
export const useDebounced = (callback, deps, delay = 167) =>
  useMemo(() => smartDebounce(callback, delay), [...deps, delay])

FYI

这是另一个PoC实现:

没有任何库(例如lodash)用于debound 使用React Hooks API

我希望它能帮助你:)

import React, { useState, useEffect, ChangeEvent } from 'react';

export default function DebouncedSearchBox({
  inputType,
  handleSearch,
  placeholder,
  debounceInterval,
}: {
  inputType?: string;
  handleSearch: (q: string) => void;
  placeholder: string;
  debounceInterval: number;
}) {
  const [query, setQuery] = useState<string>('');
  const [timer, setTimer] = useState<NodeJS.Timer | undefined>();

  useEffect(() => {
    if (timer) {
      clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      handleSearch(query);
    }, debounceInterval));
  }, [query]);

  const handleOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setQuery(e.target.value);
  };

  return (
    <input
      type={inputType || 'text'}
      className="form-control"
      placeholder={placeholder}
      value={query}
      onChange={handleOnChange}
    />
  );
}

下面是一个使用@Abra方法封装在函数组件中的代码片段 (我们使用织物的UI,只是用一个简单的按钮替换它)

import React, { useCallback } from "react";
import { debounce } from "lodash";

import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';

const debounceTimeInMS = 2000;

export const PrimaryButtonDebounced = (props) => {

    const debouncedOnClick = debounce(props.onClick, debounceTimeInMS, { leading: true });

    const clickHandlerDebounced = useCallback((e, value) => {

        debouncedOnClick(e, value);

    },[]);

    const onClick = (e, value) => {

        clickHandlerDebounced(e, value);
    };

    return (
        <PrimaryButton {...props}
            onClick={onClick}
        />
    );
}