
本文介绍如何在 React + react-hook-form + Yup 的组合中,通过 Yup 的 test() 自定义验证方法,检查用户输入是否属于指定的白名单数组(如姓名列表),并给出完整可运行的实现方案。
本文介绍如何在 react + react-hook-form + yup 的组合中,通过 yup 的 `test()` 自定义验证方法,检查用户输入是否属于指定的白名单数组(如姓名列表),并给出完整可运行的实现方案。
在表单验证场景中,除了基础的必填、格式校验外,常需进行“值存在性校验”——例如限制用户只能输入系统预设的姓名、角色或状态值。Yup 提供了灵活的 .test() 方法,支持完全自定义同步/异步验证逻辑,是实现此类需求的理想选择。
在你的代码中,只需将原有的 yup.string().required(...) 链式调用扩展为:先定义基础类型与必填规则,再通过 .test() 插入数组包含性判断。注意校验顺序——建议将 .required() 放在 .test() 之前,确保空值优先被拦截(避免 names.includes(undefined) 报错);若需空值也参与 test 判断,可将 .required() 移至 .test() 后,并在 test 内部显式处理 null/undefined。
以下是优化后的 Schema 定义示例:
const names = ['John', 'Paul', 'Mike', 'Adam'];
const schema = yup.object().shape({
firstname: yup
.string()
.required('First Name Required')
.test('is-in-names', 'Name must be in the allowed list!', (value) => {
// 安全处理:空字符串或 undefined 应已由 required 拦截,此处 value 为 string | null | undefined
if (!value || typeof value !== 'string') return false;
return names.includes(value.trim());
}),
});✅ 关键说明:
- test() 接收三个参数:name(用于调试的标识符)、message(错误提示,支持函数形式动态返回)、testFn(执行校验的函数,返回布尔值);
- 使用 value.trim() 可避免因首尾空格导致误判;
- 错误消息支持 i18n,推荐使用函数形式(如 () => t('validation.nameInvalid'))便于后续国际化;
- 若需忽略大小写匹配,可改为 names.map(n => n.toLowerCase()).includes(value.toLowerCase().trim());
- 如数组来自 API 动态加载,需结合 yup.lazy() 或自定义异步 resolver 实现,但需注意 react-hook-form 默认仅支持同步校验器。
最后,确保 yupResolver(schema) 正确注入 useForm,错误信息将自动通过 errors.firstname.message 渲染。该方案轻量、可复用,且完全兼容 Yup 的验证生态(如 .when() 条件校验、.oneOf() 等),是构建受控表单白名单校验的标准实践。

















