0

我有这个简单的组件来检查用户名是否有效。它通过在输入值更改时查询 firebase 来实现。它有一个问题。当我在输入字段中输入太快时,其中的值没有足够的时间来改变,所以它只是错过了一些字符。这是代码:

对于状态管理,我使用 Recoil.JS。

组件代码:

export const UsernameInput = (props: {
  topLabel: string;
  bottomLabel?: string;
  placeholder?: string;
  className?: string;
  valueIn: any;
  valueOut: any;
  valid: any;
  validIn: boolean;
}) => {
  const usernameRef = db.collection("usernames");
  const query = usernameRef.where("username", "==", props.valueIn);

  useEffect(() => {
    query
      .get()
      .then((querySnapshot) => {
        if (querySnapshot.size >= 1) {
          props.valid(false);
        } else {
          props.valid(true);
        }
      })
  }, [props.valueIn]);

  function handleChange(event: any) {
    props.valueOut(event.target.value);
  }

  return (
    <InputSkeleton
      topLabel={props.topLabel}
      bottomLabel={props.bottomLabel}
      className={props.className}
    >
      <div className="input-username">
        <input type="text" onChange={handleChange} value={props.valueIn} />
        <span className="text">
          <span className={props.validIn ? "available" : "taken"}></span>
          {props.validIn ? "Available" : "Taken"}
        </span>
      </div>
    </InputSkeleton>
  );
};
<UsernameInput
  className="stretch"
  topLabel="Username"
  valueIn={formD.username}
  valueOut={(value: string) => {
    setFormD({ ...formD, username: value });
  }}
  valid={(value: boolean) => {
    setFormD({ ...formD, usernameValid: value });
  }}
  validIn={formD.usernameValid}
  bottomLabel="This will be your unique handle on xyz.com"
/>
4

3 回答 3

0

创建一个简单的去抖动函数,它以函数和时间(以秒为单位)为参数:

export function debounce(func, wait) {
    let timeout;

    return function executedFunction(...args) {
        const later = () => {
            timeout = null;

            func(...args);
        };
        clearTimeout(timeout);

        timeout = setTimeout(later, wait);
    };
}

然后在您的事件处理程序handleChange函数中使用它:

function handleChange(event: any) {
   event.preventDefault();
   // This means that you want to update the value after 500 milliseconds, i.e when you're sure that the user has stopped typing. You can extend this time to whatever figure you want
   debounce(props.valueOut(event.target.value), 500);
}
于 2021-05-25T16:06:02.503 回答
0

将此变量放在UsernameInput函数之外

const WAIT_INTERVAL = 1000;

编辑您的句柄更改为此

componentWillMount() {
    this.timer = null;
}

function handleChange(event: any) {
    clearTimeout(this.timer);
    this.timer = setTimeout(props.valueOut(event.target.value), WAIT_INTERVAL);
}
于 2021-05-25T16:09:30.963 回答
0

Princewill 的想法是正确的,但实施需要稍作调整。具体来说,您需要在多次调用 时保留计时器句柄debounce,并且参数 todebounce需要是一个实际函数。使用普通函数不会这样做,因为每次调用都会导致不同的本地超时句柄,并且旧句柄永远不会被取消或更新。

我建议改编或使用 useHooks 中的useDebounce钩子。这使用 useEffect 来利用 React 的效果卸载来清除任何先前设置的超时,并且总体上非常清晰。

const { valueIn, valueOut } = props;
const [username, setUsername] = useState<string>(valueIn);
// On each event, update `username`
const handleChange = useCallback(
  (event: any) => setUsername(event.target.value),
  [setUsername]
);

// Collect changes to username and change debouncedUsername to the latest
// value after a change has not been made for 500ms.
const debouncedUsername = useDebounce(username, 500);

// Each time debouncedUsername changes, run the desired callback
useEffect(() => {
  if (debouncedUsername !== valueIn) {
    valueOut(debouncedUsername);
  }
}, [valueIn, valueOut, debouncedUsername]);

这里的想法是:

  1. 您通过 useState 保留字段状态的实时更新副本
  2. 您通过 useDebounce 保留字段状态的延迟更新副本
  3. 当延迟更新的副本最终更改时, useEffect 会触发您的 valueOut 回调。在构造时,这将在username更改后触发,但在 500 毫秒内没有再次更改。

此外,您可能希望将字段设置valueusername,而不是valueIn,以便该字段实时更新,而不是延迟更新。

于 2021-05-25T16:56:40.380 回答