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

当前回答

如果你只需要从事件对象中获取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} />
    }
}

其他回答

我的解决方案是基于钩子(用Typescript编写)。

我有2个主要钩子useDebouncedValue和useDebouncedCallback

First - useDebouncedValue

假设我们有一个搜索框,但是我们想在用户停止输入0.5秒后向服务器请求搜索结果

function SearchInput() {
  const [realTimeValue, setRealTimeValue] = useState('');

  const debouncedValue = useDebouncedValue(realTimeValue, 500); // this value will pick real time value, but will change it's result only when it's seattled for 500ms

  useEffect(() => {
    // this effect will be called on seattled values
    api.fetchSearchResults(debouncedValue);
  }, [debouncedValue])

  return <input onChange={event => setRealTimeValue(event.target.value)} />
}

实现

import { useState, useEffect } from "react";

export function useDebouncedValue<T>(input: T, time = 500) {
  const [debouncedValue, setDebouncedValue] = useState(input);

  // every time input value has changed - set interval before it's actually commited
  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedValue(input);
    }, time);

    return () => {
      clearTimeout(timeout);
    };
  }, [input, time]);

  return debouncedValue;
}

第二个useDebouncedCallback

它只是在你的组件范围内创建一个“debpublished”函数。

假设我们有一个带有按钮的组件,在你停止点击它后,它将显示警报500毫秒。

function AlertButton() {
  function showAlert() {
    alert('Clicking has seattled');
  }

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}

实现(注意我使用lodash/debounce作为辅助)

import debounce from 'lodash/debounce';
import { useMemo } from 'react';

export function useDebouncedCallback<T extends (...args: any) => any>(callback: T, wait?: number) {
  const debouncedCallback = useMemo(() => debounce(callback, wait), [callback, wait]);

  return debouncedCallback;
}

这个解决方案不需要任何额外的库,它也会在用户按下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}  />

如果我们有兴趣在react.js中使用epic.js,我们可以在epic.js库中使用debounceTime rxjs/操作符。因此,我们可以在epic.js的帮助下,在react.js中使用可观察对象,而不是使用回调。

Julen的解决方案有点难以阅读,这里有更清晰和准确的反应代码,供那些根据标题而不是问题的微小细节绊倒他的人使用。

Tl;dr版本:当你要更新到观察者发送调用一个调度方法,反过来实际上会通知观察者(或执行ajax等)

使用示例组件jsfiddle完成jsfiddle

var InputField = React.createClass({

    getDefaultProps: function () {
        return {
            initialValue: '',
            onChange: null
        };
    },

    getInitialState: function () {
        return {
            value: this.props.initialValue
        };
    },

    render: function () {
        var state = this.state;
        return (
            <input type="text"
                   value={state.value}
                   onChange={this.onVolatileChange} />
        );
    },

    onVolatileChange: function (event) {
        this.setState({ 
            value: event.target.value 
        });

        this.scheduleChange();
    },

    scheduleChange: _.debounce(function () {
        this.onChange();
    }, 250),

    onChange: function () {
        var props = this.props;
        if (props.onChange != null) {
            props.onChange.call(this, this.state.value)
        }
    },

});

如果你不喜欢添加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)}
    />
  );
}