如何使用 React hooks 去抖动 onChange 事件。

tl;博士

  useEffect(() => {
    const timeout = setTimeout(() => someFunction(), 400);
    return () => { clearTimeout(timeout); };
  }, [dependency]);

在效果中使用 setTimeout 并在下一次效果调用之前将其清除。这只会在依赖数组 400 毫秒未更改时调用 someFunction()。这种模式非常适合发出网络请求或调用其他昂贵的函数。

重构滞后的输入

用户名组件有两种状态:用户名和有效。在每次输入更改时,我们将用户名设置为新值,并计算并设置有效性。

这可行,但这是一个糟糕的用户体验,因为输入感觉非常滞后。验证需要很长时间,并且键盘事件感觉不是即时的。

在这里,我正在用一个大的 for 循环来烧循环,但你可以想象在它的位置发出一个网络请求。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const validate = value => {
  // expensive validation
  for (var x = 1; x < 500000000; x++) {
    value.length < x;
  }

  if (value.length > 5) {
    return "max length is 5";
  }

  if (value.length === 0) {
    return "please select your username";
  }

  return "looks good";
};


const Username = () => {
  const [username, setUsername] = useState("");
  const [valid, setValid] = useState(undefined);

  return (
    <div>
      <div>Username</div>
      <input
        type="text"
        value={username}
        onChange={e => {
          const value = e.target.value;
          setUsername(value);
          setValid(validate(value));
        }}
      />
      <div>{valid}</div>
    </div>
  );
};

ReactDOM.render(<Username />, document.getElementById("main"));

重构消除滞后

我们仍然想检查用户名是否有效,但我们想让它为用户提供良好的体验。目标是仅在输入暂停后验证用户输入。我们不关心在用户输入时验证输入,我们只想在他们暂停片刻时验证它。

从 onChange 事件中移除 setValid 调用。

我们只想在用户停止输入一段时间后调用 setValid。现在,onChange 只更新用户名。输入将不再感觉滞后,但不会触发验证。

** useEffect 钩子**

我们将使用“useEffect”钩子来计算和设置验证。 useEffect 钩子有两个参数,效果函数和依赖数组。当依赖数组中的值发生变化时,效果会触发。在我们的例子中,我们想在用户名改变时触发回调,所以我们把它放在依赖数组中。

仍然滞后

  useEffect(() => {
    setValid(validate(username));
  }, [username]);

只要用户名更改,效果就会触发。我们无法检查和设置效果中的有效性,因为我们将面临与以前相同的问题:输入滞后。因此,我们需要一种方法,仅在用户名在一定时间内未更改后才调用验证函数。

使用 setTimeout

setTimeout 有两个参数:回调和毫秒数。在此示例中,我们要检查并设置回调中的有效性。我选择了 400 毫秒作为超时。现在,每次用户名更改时都会设置超时并触发验证。

还是不好:

  useEffect(() => {
    const timer = setTimeout(() => {
      setValid(validate(username));
    }, 400);
  }, [username]);

不过,这并不理想。每次按键都会产生超时,并且每次按键都会调用验证,只是延迟了一点。

使用效果清理

useEffect 提供了一种清理效果的方法。如果你从一个效果返回一个函数,那么它将在下一个效果之前触发。这正是我们所需要的。我们可以在创建新超时之前返回一个清除旧超时的函数。

  useEffect(() => {
    setValid("");
    const timer = setTimeout(() => {
      setValid(validate(username));
    }, 400);

    return () => {
      clearTimeout(timer);
    };
  }, [username]);

这是您仅在用户名 400 毫秒未更改后调用验证函数的方式。

完整代码

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

const validate = value => {
  // expensive validation
  for (var x = 1; x < 500000000; x++) {
    value.length < x;
  }

  if (value.length > 5) {
    return "max length is 5";
  }

  if (value.length === 0) {
    return "please select your username";
  }

  return "looks good";
};

const Username = () => {
  const [username, setUsername] = useState("");
  const [valid, setValid] = useState(undefined);

  useEffect(() => {
    // clear the valid message so nothing is displayed while typing
    setValid("");

    // create the timer
    const timer = setTimeout(() => {
      setValid(validate(username));
    }, 400);

    // return a cleanup function that clears the timeout
    return () => {
      clearTimeout(timer);
    };
  }, [username]);

  return (
    <div>
      <div>Username</div>
      <input
        type="text"
        value={username}
        onChange={e => {
          const value = e.target.value;
          setUsername(value);
        }}
      />
      <div>{valid}</div>
    </div>
  );
};

延伸阅读

查看 useEffect 的反应文档:https://reactjs.org/docs/hooks-effect.html

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐