useEffect 中的 debounce 问题

Ryn*_*yne 23 reactjs debounce react-hooks

我有一个带有用户名输入的表单,我正在尝试验证用户名是否在去抖动功能中使用。我遇到的问题是,当我输入“user”时,我的去抖动似乎不起作用,我的控制台看起来像

u
us
use
user
Run Code Online (Sandbox Code Playgroud)

这是我的去抖功能

export function debounce(func, wait, immediate) {
    var timeout;

    return () => {
        var context = this, args = arguments;

        var later = () => {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };

        var callNow = immediate && !timeout;

        clearTimeout(timeout);

        timeout = setTimeout(later, wait);

        if (callNow) func.apply(context, args);
    };
};
Run Code Online (Sandbox Code Playgroud)

这是我在 React 组件中调用它的方式

import React, { useEffect } from 'react' 

// verify username
useEffect(() => {
    if(state.username !== "") {
        verify();
    }
}, [state.username])

const verify = debounce(() => {
    console.log(state.username)
}, 1000);
Run Code Online (Sandbox Code Playgroud)

去抖功能似乎是正确的?我在反应中调用它的方式有问题吗?

El *_*mza 40

每次您的组件重新渲染时,都会创建一个新的去抖动verify函数,这意味着useEffect您实际上在内部调用了不同的函数,这与去抖动的目的背道而驰。

就像你在做这样的事情:

const debounced1 = debounce(() => { console.log(state.username) }, 1000);
debounced1();

const debounced2 = debounce(() => { console.log(state.username) }, 1000);
debounced2();

const debounced3 = debounce(() => { console.log(state.username) }, 1000);
debounced3();
Run Code Online (Sandbox Code Playgroud)

与您真正想要的相反:

const debounced = debounce(() => { console.log(state.username) }, 1000);
debounced();
debounced();
debounced();
Run Code Online (Sandbox Code Playgroud)

解决此问题的一种方法是使用useCallbackwhich 将始终返回相同的回调(当您将空数组作为第二个参数传递时),此外,我会将 传递username给此函数而不是访问内部状态(否则您将访问陈旧状态):

import { useCallback } from "react";
const App => () {
  const [username, setUsername] = useState("");

  useEffect(() => {
    if (username !== "") {
      verify(username);
    }
  }, [username]);

  const verify = useCallback(
    debounce(name => {
      console.log(name);
    }, 200),
    []
  );

  return <input onChange={e => setUsername(e.target.value)} />;
}
Run Code Online (Sandbox Code Playgroud)

您还需要稍微更新您的 debounce 函数,因为它没有正确地将参数传递给 debounced 函数。

function debounce(func, wait, immediate) {
  var timeout;

  return (...args) => { <--- needs to use this `args` instead of the ones belonging to the enclosing scope
    var context = this;
...
Run Code Online (Sandbox Code Playgroud)

演示


小智 20

export function useLazyEffect(effect: EffectCallback, deps: DependencyList = [], wait = 300) {
  const cleanUp = useRef<void | (() => void)>();
  const effectRef = useRef<EffectCallback>();
  const updatedEffect = useCallback(effect, deps);
  effectRef.current = updatedEffect;
  const lazyEffect = useCallback(
    _.debounce(() => {
      cleanUp.current = effectRef.current?.();
    }, wait),
    [],
  );
  useEffect(lazyEffect, deps);
  useEffect(() => {
    return () => {
      cleanUp.current instanceof Function ? cleanUp.current() : undefined;
    };
  }, []);
}
Run Code Online (Sandbox Code Playgroud)

  • 不错的通用版!这个答案值得更多的赞成。也许添加一个关于如何使用它的示例(即使它看起来很明显)。也许还可以添加一些解释性注释。 (2认同)
  • 该函数包含在一个很好的要点中,带有导入并可以使用:) https://gist.github.com/felipecsl/afb987f8b6059814cff0a2ca6020e108 (2认同)

Sam*_*l G 8

我建议做一些改变。

1) 每次进行状态更改时,都会触发渲染。每个渲染都有自己的道具和效果。因此,useEffect每次更新用户名时,都会生成一个新的去抖动函数。这是useCallback钩子在渲染之间保持函数实例相同的一个很好的例子,或者可能是useRef - 我自己坚持使用 useCallback。

2)我会分离出单独的处理程序,而不是useEffect用来触发你的去抖动——随着组件的增长,你最终会得到一长串依赖项,这不是最好的地方。

3)您的去抖动功能不处理参数。(我用 lodash.debouce 代替,但你可以调试你的实现)

4)我认为你仍然想更新按键状态,但只每 x 秒运行一次你被谴责的函数

例子:

import React, { useState, useCallback } from "react";
import "./styles.css";
import debounce from "lodash.debounce";

export default function App() {
  const [username, setUsername] = useState('');

  const verify = useCallback(
    debounce(username => {
      console.log(`processing ${username}`);
    }, 1000),
    []
  );

  const handleUsernameChange = event => {
    setUsername(event.target.value);
    verify(event.target.value);
  };

  return (
    <div className="App">
      <h1>Debounce</h1>
      <input type="text" value={username} onChange={handleUsernameChange} />
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

演示

我强烈推荐阅读这篇关于 useEffect 和 hooks 的好文章