如何在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
  }
});

当前回答

如果你不喜欢添加lodash或任何其他包:

import React, { useState, useRef } from "react";

function DebouncedInput() {
  const [isRefetching, setIsRefetching] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");
  const previousSearchTermRef = useRef("");

  function setDebouncedSearchTerm(value) {
    setIsRefetching(true);
    setSearchTerm(value);
    previousSearchTermRef.current = value;
    setTimeout(async () => {
      if (previousSearchTermRef.current === value) {
        try {
          // await refetch();
        } finally {
          setIsRefetching(false);
        }
      }
    }, 500);
  }

  return (
    <input
      value={searchTerm}
      onChange={(event) => setDebouncedSearchTerm(event.target.value)}
    />
  );
}

其他回答

如果你正在使用redux,你可以通过中间件以一种非常优雅的方式做到这一点。你可以这样定义Debounce中间件:

var timeout;
export default store => next => action => {
  const { meta = {} } = action;
  if(meta.debounce){
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      next(action)
    }, meta.debounce)
  }else{
    next(action)
  }
}

然后你可以添加debounging到动作创建者,比如:

export default debouncedAction = (payload) => ({
  type : 'DEBOUNCED_ACTION',
  payload : payload,
  meta : {debounce : 300}
}

实际上已经有中间件你可以脱离npm来为你做这件事。

与其在debounce()中包装handleOnChange,不如在debounce()中包装回调函数中的ajax调用,从而不破坏事件对象。就像这样:

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}

您可以使用引用变量来存储计时器,然后将其清除。下面是一个不使用任何第三方包在react中实现deboundation的例子

import { useState, useRef } from "react";
import "./styles.css";

export default function App() {
  // Variables for debouncing
  const [text, setText] = useState("");
  const timer = useRef();

  // Variables for throtteling
  const [throttle, setThrottle] = useState(false)


  const handleDebouncing = ({ target }) => {
    clearTimeout(timer.current)

    timer.current = setTimeout(() => {
      callApi();
    }, 300);

    setText(target.value);
  };

  const handleThrottleing = () => {
    callApi()

    setThrottle(true)

    setTimeout(() => {
      setThrottle(false)
    }, 2000)
  }

  const callApi = () => {
    console.log("Calling Api");
  };

  return (
    <div className="App">
      <input type="text" onChange={handleDebouncing} />

      <button onClick={handleThrottleing} disabled={throttle} >Click me to see throtteling</button>
    </div>
  );
}

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}
    />
  );
}

这里有一个工作的TypeScript示例,供那些使用TS并想要debounce async函数的人使用。

function debounce<T extends (...args: any[]) => any>(time: number, func: T): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
     let timeout: Timeout;

     return (...args: Parameters<T>): Promise<ReturnType<T>> => new Promise((resolve) => {
         clearTimeout(timeout);
         timeout = setTimeout(() => {
             resolve(func(...args));
         }, time)
     });
 }