Async Validator 使用技巧与常见问题解决方案

UX艳丽 交互 阅读 1,865
赞 10 收藏
二维码
手机扫码查看
反馈

Async Validator踩坑实录:异步校验的那些糟心事

最近在做一个表单校验的需求,涉及到用户名的异步唯一性校验。说起来简单,就是用户输入完用户名后,去后端查一下这个用户名有没有被注册过。结果这玩意儿折腾了我大半天,这里把踩坑过程记录一下。

Async Validator 使用技巧与常见问题解决方案

先说问题:用Ant Design的Form组件配合async-validator做校验时,发现异步校验总是不生效。具体表现是:第一次输入用户名时校验正常,但修改后再输入就没反应了。这里我踩了个坑,以为是组件的问题,换了好几个库都没解决。

排查过程:从怀疑到真相

最开始我以为是Ant Design的问题,毕竟它封装了一层校验逻辑。试了下直接用原生的async-validator,发现问题依然存在。又怀疑是不是Promise写法有问题,改成async/await也不行。

折腾了半天发现,问题出在校验规则的返回值上。我最初的写法是这样的:

const validateUsername = (_, value) => {
  return new Promise((resolve, reject) => {
    fetch(https://jztheme.com/api/check-username?username=${value})
      .then(res => res.json())
      .then(data => {
        if (data.exists) {
          reject(new Error('用户名已存在'));
        } else {
          resolve();
        }
      });
  });
};

看着好像没问题,但实际上有个大坑:当用户快速输入时,多个请求会同时发出,导致后面的请求覆盖前面的结果。这就造成了有时候校验结果显示异常的情况。

核心代码就这几行:最终解决方案

后来试了下发现,得加个取消机制。这里是改进后的完整代码:

let currentRequest = null;

const validateUsername = (_, value) => {
  // 取消之前的请求
  if (currentRequest) {
    currentRequest.abort();
  }

  return new Promise((resolve, reject) => {
    currentRequest = fetch(https://jztheme.com/api/check-username?username=${value}, {
      signal: new AbortController().signal
    });

    currentRequest
      .then(res => res.json())
      .then(data => {
        if (data.exists) {
          reject(new Error('用户名已存在'));
        } else {
          resolve();
        }
      })
      .catch(err => {
        if (err.name !== 'AbortError') {
          console.error('校验出错', err);
          reject(new Error('校验失败,请稍后重试'));
        }
      });
  });
};

这里有几个关键点要注意:

  • AbortController:用来取消未完成的请求,防止请求堆积
  • 错误处理:要区分是用户主动取消还是其他错误
  • 防抖处理:虽然加了取消机制,但最好还是加个防抖,减少不必要的请求

一些延伸思考:为什么这么设计

其实这个方案还不是最完美的。比如说,如果用户网络特别慢,可能第一个请求还没返回就被第二个请求取消了。不过在实际项目中,这种情况出现的概率不大,暂时可以接受。

这里再补充一个细节:很多人喜欢在校验函数里直接return Promise,但这种方式在复杂场景下很容易出问题。建议统一用resolve和reject来处理,逻辑会更清晰。

顺便提一下性能优化的小技巧:可以在本地维护一个Set,缓存已经校验过的用户名。这样同一个用户名多次输入时就不需要重复请求了:

const usernameCache = new Set();

const validateUsername = (_, value) => {
  if (usernameCache.has(value)) {
    return Promise.resolve();
  }

  // 其余逻辑不变...
};

踩坑提醒:这三点一定注意

总结一下容易踩坑的地方:

  • 不要忽略请求取消机制,否则会出现”幽灵请求”
  • 错误处理要细致,特别是网络异常情况
  • 记得加防抖和缓存,提升用户体验的同时也能减轻服务器压力

以上是我踩坑后的总结,如果你有更好的方案欢迎评论区交流。这个需求虽然看起来简单,但要做好还是挺考验基本功的。前端开发就是这样,越基础的东西越容易出幺蛾子。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论