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

当前回答

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

其他回答

2022 -使用useEffect钩子

此时最好的选择是使用useEffect钩子。useEffect允许您设置一个函数,该函数可以修改状态以响应某些异步事件。debounging是异步的,因此useEffect可以很好地用于此目的。

如果从钩子返回一个函数,返回的函数将在钩子再次被调用之前被调用。这让您可以取消之前的超时,有效地解除函数。

例子

这里我们有两个状态,value和tempValue。设置tempValue将触发useEffect钩子,该钩子将启动一个1000ms超时,该超时将调用一个函数将tempValue复制为value。

钩子返回一个取消定时器设置的函数。当钩子再次被调用时(即按下另一个键),超时被取消并重置。

const DebounceDemo = () => {
  const [value, setValue] = useState();
  const [tempValue, setTempValue] = useState();

  // This hook will set a 1000 ms timer to copy tempValue into value
  // If the hook is called again, the timer will be cancelled
  // This creates a debounce
  useEffect(
    () => {
      // Wait 1000ms before copying the value of tempValue into value;
      const timeout = setTimeout(() => {
        setValue(tempValue);
      }, 1000);

      // If the hook is called again, cancel the previous timeout
      // This creates a debounce instead of a delay
      return () => clearTimeout(timeout);
    },
    // Run the hook every time the user makes a keystroke
    [tempValue]
  )

  // Here we create an input to set tempValue. 
  // value will be updated 1000 ms after the hook is last called, 
  // i.e after the last user keystroke.
  return (
    <>
      <input 
        onChange={
          ({ target }) => setTempValue(target.value)
        }
      />
      <p>{ value }</p>
    </>
  )
}

避免使用event.persist()——你想让React回收合成事件。我认为无论你使用类还是钩子,最干净的方法是将回调函数分成两部分:

没有deboundation的回调 只使用您需要的事件片段调用已撤销的函数(这样合成的事件就可以循环使用)

handleMouseOver = throttle(target => {
  console.log(target);
}, 1000);

onMouseOver = e => {
  this.handleMouseOver(e.target);
};

<div onMouseOver={this.onMouseOver} />

功能

const handleMouseOver = useRef(throttle(target => {
  console.log(target);
}, 1000));

function onMouseOver(e) {
  handleMouseOver.current(e.target);
}

<div onMouseOver={this.onMouseOver} />

注意,如果你的handleMouseOver函数从组件中使用状态,你应该使用usemo而不是useRef,并将它们作为依赖项传递,否则你将使用过时的数据(当然不适用于类)。

简单有效 https://www.npmjs.com/package/use-debounce use-debounce

import { useDebouncedCallback } from 'use-debounce';

function Input({ defaultValue }) {
  const [value, setValue] = useState(defaultValue);
  const debounced = useDebouncedCallback(
    (value) => {
      setValue(value);
    },
    // delay
    1000
  );

  return (
    <div>
      <input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />
      <p>Debounced value: {value}</p>
    </div>
  );
}

这个解决方案不需要任何额外的库,它也会在用户按下enter时启动:

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

const [search, setSearch] = useState('');
const [searchFor, setSearchFor] = useState(search);

useEffect(() => {
    console.log("Search:", searchFor);
}, [searchFor]);

const fireChange = event => {
    const { keyCode } = event;
    if (keyCode === 13) {
        event.preventDefault();
        setSearchFor(search);
    }
}

const changeSearch = event => {
    const { value } = event.target;
    setSearch(value);
    debounceSetSearchFor(value);
};

const debounceSetSearchFor = useCallback(debounce(function(value) {
    setSearchFor(value);
}, 250), []);

输入可以是这样的:

<input value={search} onKeyDown={fireChange} onChange={changeSearch}  />

如果你只需要从事件对象中获取DOM输入元素,解决方案就简单多了——只需要使用ref。注意,这需要下划线:

class Item extends React.Component {
    constructor(props) {
        super(props);
        this.saveTitle = _.throttle(this.saveTitle.bind(this), 1000);
    }
    saveTitle(){
        let val = this.inputTitle.value;
        // make the ajax call
    }
    render() {
        return <input 
                    ref={ el => this.inputTitle = el } 
                    type="text" 
                    defaultValue={this.props.title} 
                    onChange={this.saveTitle} />
    }
}