Lodash辩解没有在React工作

时间:2017-12-14 09:14:26

标签: javascript reactjs ecmascript-6 lodash debounce

最好首先查看我的代码:

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(如果可用),设置组件的本地状态。

然后在onChange的{​​{1}}函数中更新状态,然后尝试调用webservice端点以使用input设置新值。

如果我直接通过输入的Services.setValue()设置debounce,那么工作是什么:

onChange

但是<input value={this.state.value} onChange={_.debounce((event, value) => this.onChange(value), 1000)} /> 仅在每1000毫秒被调用并更新视图。因此,在文本字段中输入最终看起来很奇怪,因为您键入的内容仅在稍后显示。

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

3 个答案:

答案 0 :(得分:26)

问题出现是因为您没有调用去抖功能,您可以按以下方式进行操作

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

答案 1 :(得分:1)

为那些因为油门/防抖功能不适用于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} />;
};

This medium article完美地解释了会发生什么:

函数内部的局部变量在每次调用后到期。每次 重新评估组件,初始化局部变量 再次。油门和反跳功能在后面使用window.setTimeout() 现场。每次评估功能组件时,您 注册新的setTimeout回调。 因此,我们将使用useRef()钩子,因为useRef()返回的值不会在每次执行功能组件时得到重新评估。唯一的不便是您必须通过.current属性访问存储的值。

我已经使用小型lodash.throttlelodash.debounce软件包创建了sandbox,因此您可以对两者进行试验并选择合适的行为

答案 2 :(得分:1)

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

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

useCallback利用去抖动返回的功能并按预期工作。 但是,当您要在去抖动功能中使用状态变量时,情况会有些复杂(通常是这种情况)。

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

React.useCallback的第二个参数用于依赖项。如果您想在去抖动功能中使用状态变量或prop变量,则默认情况下,它会使用状态变量的旧版本,这会使您的函数使用不需要的变量的历史值。 要解决此问题,您必须像在React.useEffect中那样包含状态变量,如下所示:

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

此实现可能会解决您的目的。但是您会注意到,每当依赖项更改时传递的状态变量(stateVariable1,stateVariable2)都会调用去抖动功能。可能不是您所需要的,特别是如果使用诸如输入字段之类的受控组件。

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

constructor(props)
    {
        super();
        this.state = {...};
        this.functionToCall= debounce(this.functionToCall.bind(this), 400, {'leading': true});
    }