31

最好先看看我的代码:

import React, { Component } from 'react';
import _ from 'lodash';
import Services from 'Services'; // Webservice calls

export default class componentName extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: this.props.value || null
    }
  }

  onChange(value) {
    this.setState({ value });

    // This doesn't call Services.setValue at all
    _.debounce(() => Services.setValue(value), 1000);
  }

  render() {
    return (
      <div>
        <input 
          onChange={(event, value) => this.onChange(value)}
          value={this.state.value}
        />
      </div>
    )
  }
}

只是一个简单的输入。在构造函数中,它value从 props(如果可用)中获取组件的本地状态。

然后在我更新状态的onChange函数中input,然后尝试调用 web 服务端点来设置新值Services.setValue()

如果我像这样debounce直接通过输入设置onChange

<input 
  value={this.state.value} 
  onChange={_.debounce((event, value) => this.onChange(value), 1000)} 
/>

但随后this.setState仅每 1000 毫秒调用一次并更新视图。因此,在文本字段中输入最终看起来很奇怪,因为您输入的内容只在一秒钟后显示。

在这种情况下我该怎么办?

4

3 回答 3

53

出现问题是因为您没有调用 debounce 函数,您可以按以下方式进行

export default class componentName extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: this.props.value || null
    }
    this.servicesValue = _.debounce(this.servicesValue, 1000);
  }

  onChange(value) {
    this.setState({ value });
    this.servicesValue(value);
  }
  servicesValue = (value) => {
      Services.setValue(value)
  }
  render() {
    return (
      <div>
        <input 
          onChange={(event, value) => this.onChange(value)}
          value={this.state.value}
        />
      </div>
    )
  }
}
于 2017-12-14T09:21:34.327 回答
25

对于那些因为油门/去抖不起作用而来到这里的人的解决方案FunctionComponent- 您需要通过useRef()存储去抖功能:

export const ComponentName = (value = null) => {
  const [inputValue, setInputValue] = useState(value);

  const setServicesValue = value => Services.setValue(value);

  const setServicesValueDebounced = useRef(_.debounce(setServicesValue, 1000));

  const handleChange = ({ currentTarget: { value } }) => {
    setInputValue(value);
    setServicesValueDebounced.current(value);
  };

  return <input onChange={handleChange} value={inputValue} />;
};

这篇中篇文章完美地解释了会发生什么:

函数内的局部变量在每次调用后都会过期。每次重新评估组件时,局部变量都会再次初始化。Throttle 和 debouncewindow.setTimeout()在幕后使用。每次评估函数组件时,您都在注册一个新的setTimeout回调。所以我们将使用useRef()钩子作为返回的值,useRef()不会在每次执行功能组件时重新评估。.current唯一的不便是您必须通过该物业访问您的储值。

我创建了带有小包和包的沙箱,因此您可以尝试两者并选择合适的行为lodash.throttlelodash.debounce

于 2020-11-16T10:11:26.887 回答
7

对于 React 功能组件,debounce 默认情况下不起作用。您必须执行以下操作才能使其正常工作:

const debouncedFunction= React.useCallback(debounce(functionToCall, 400), []);

useCallback 使用 debounce 返回的函数并按预期工作。虽然,当您想在去抖动函数中使用状态变量时,这有点复杂(通常是这种情况)。

React.useCallback(debounce(fn, timeInMs), [])

React.useCallback 的第二个参数是依赖关系。如果您想在 debounced 函数中使用 state 或 prop 变量,默认情况下,它使用旧版本的 state 变量,这将导致您的函数使用变量的历史值,这不是您需要的。要解决此问题,您必须像在 React.useEffect 中一样包含状态变量,如下所示:

React.useCallback(debounce(fn, timeInMs), [stateVariable1, stateVariable2])

此实现可能会解决您的目的。但是您会注意到,每当状态变量(stateVariable1、stateVariable2)随着依赖关系的变化而传递时,都会调用 debounced 函数。这可能不是您需要的,尤其是在使用输入字段等受控组件时。

我意识到的最佳解决方案是花一些时间将功能组件更改为基于类的组件并使用以下实现:

constructor(props)
    {
        super();
        this.state = {...};
        this.functionToCall= debounce(this.functionToCall.bind(this), 400, {'leading': true});
    }
于 2020-11-16T14:29:45.833 回答