反应输入字段现在可以在输入太快时工作

React input field does now work when typing too fast

我有这个简单的组件来检查用户名是否有效。它通过在输入值更改时查询 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"
/>

创建一个简单的 debounce 函数,该函数以秒为单位的函数和时间作为参数:

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

将此变量放在 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);
}

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

我建议改编或使用 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 毫秒内没有再次更改。

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