微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当输入太快时,React 输入字段现在可以工作

如何解决当输入太快时,React 输入字段现在可以工作

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

解决方法

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,以便实时更新该字段,而不是延迟更新。

,

创建一个简单的 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);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。