簡體   English   中英

Lodash debounce 在 React 中不起作用

[英]Lodash debounce not working in React

最好先看看我的代碼:

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中,它在為組件設置本地 state 時從道具(如果可用)中獲取值。

然后在inputonChange function 中,我更新了 state 然后嘗試調用 webservice 端點以使用Services.setValue()設置新值。

如果我直接通過輸入的onChange設置debounce ,那么有效的是:

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

但隨后this.setState僅每 1000 毫秒調用一次並更新視圖。 因此,在文本字段中輸入最終看起來很奇怪,因為您輸入的內容只在一秒鍾后顯示。

在這種情況下我該怎么辦?

出現問題是因為您沒有調用 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>
    )
  }
}

對於那些因為油門/去抖不適用於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} />;
};

這篇中篇文章完美地解釋了會發生什么:

函數內的局部變量在每次調用后都會過期。 每次重新評估組件時,局部變量都會再次初始化。 節流和去抖動在幕后使用window.setTimeout()工作。 每次評估函數組件時,您都在注冊一個新的setTimeout回調。 所以我們將使用useRef()鈎子,因為useRef()返回的值不會在每次執行功能組件時重新評估。 唯一的不便是您必須通過.current屬性訪問您的存儲值。

我用微小的lodash.throttlelodash.debounce包創建了沙箱,因此您可以同時試驗並選擇合適的行為

對於 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});
    }

我為那些使用 react 功能組件的人寫了一個鈎子。

它是打字稿,但您可以忽略在您的 javascript 應用程序上使用的類型注釋。

| 使用去抖動.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} />
}

功能組件的解決方案 - 使用 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} />;
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM