javascript Lodash去抖在React中不工作

iyfjxgzm  于 2023-09-29  发布在  Java
关注(0)|答案(6)|浏览(182)

最好先看看我的代码:

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

只是一个简单的输入。在constructor中,它从props(如果可用)中获取value,为组件设置本地状态。
然后,在inputonChange函数中,我更新了状态,然后尝试调用Web服务端点来使用Services.setValue()设置新值。
如果我直接通过输入的onChange设置debounce,就可以这样做:

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

但是this.setState每1000毫秒才被调用一次,并更新视图。因此,在文本字段中键入内容最终看起来很奇怪,因为您键入的内容仅在一秒钟后显示。
在这种情况下我该怎么办?

xqk2d5yq

xqk2d5yq1#

出现问题的原因是您没有调用去抖动函数,您可以按以下方式执行操作

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

fd3cxomn2#

为那些因为throttle / debounce不适用于FunctionComponent而来到这里的人提供的解决方案-您需要通过useRef()存储debounce函数:

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完美地解释了发生了什么:
函数中的局部变量在每次调用后都会过期。每次重新计算组件时,局部变量都会再次初始化。Throttle和debounce在幕后使用window.setTimeout()工作。每次计算函数组件时,都注册一个新的setTimeout回调。因此,我们将使用useRef()钩子,因为useRef()返回的值不会在每次执行函数组件时重新计算。唯一的不便之处是您必须通过.current属性访问存储的值。
我已经用lodash.throttlelodash.debounce两个小包创建了sandbox,这样您就可以试验这两个包并选择合适的行为

ahy6op9u

ahy6op9u3#

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

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

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

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

react.useCallback的第二个参数是依赖项。如果你想在debbounced函数中使用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});
    }
nx7onnlm

nx7onnlm4#

我为那些使用react函数组件的人写了一个钩子。
它是typescript,但是你可以忽略在你的JavaScript应用程序中使用的类型注解。
我的天| use-debounce.ts |

import { debounce, DebounceSettings } from 'lodash'
import { useRef } from 'react'

interface DebouncedArgs<T> {
  delay?: number
  callback?: (value: T) => void
  debounceSettings?: DebounceSettings
}


export const useDebounce = <T = unknown>({ callback, debounceSettings, delay = 700 }: DebouncedArgs<T>) => {
  const dispatchValue = (value: T) => callback?.(value)

  const setValueDebounced = useRef(debounce(dispatchValue, delay, { ...debounceSettings, maxWait: debounceSettings?.maxWait || 1400 }))

  return (value: T) => setValueDebounced.current(value)
}

我的天|使用方法:|

export const MyInput: FC = () => {
  const [value, setValue] = useState<string>('')
  const debounce = useDebounce({ callback: onChange })

  const handleOnInput = (evt: FormEvent<HTMLInputElement>) => {
    const { value } = evt.currentTarget
    setValue(value)
    debounce(value)
  }

  function onChange(value: string) {
    // send request to the server for example
    console.log(value)
  }

  return <input value={value} onInput={handleOnInput} />
}
mpbci0fu

mpbci0fu5#

功能组件解决方案-useCallback

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

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

  const setServicesValueDebounced = useCallback(_.debounce(setServicesValue, 500), []);

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

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

jgwigjjp6#

基于类的组件的Lodash去抖动解决方案

import { Component } from 'react'
import debounce from 'lodash/debounce'

class MyComponent extends Component {

  mySearchFunction = (keyword) => {
    /* API Call or something else
      searchApi(keyword)
    */
  }

  searchWithDebounce = debounce(this.mySearchFunction, 1000)

  render () {
    return (<div>
      <input onChange={ e => this.searchWithDebounce(e.target.value) } />
    </div>)
  }

export default MyComponent

相关问题