我在实现自己的钩子时陷入了“太多重新渲染”陷阱。钩子本身看起来像这样:
function useRegexValidate(value: string, regex: RegExp, errorMessage: string, deps: any[] = []): ValidationResult {
const [result, setResult] = useState<ValidationResult>(ValidationResult.Valid);
useEffect(() => {
if (!regex.test(value)) {
setResult(new ValidationResult(false, errorMessage));
} else {
setResult(ValidationResult.Valid);
}
}, [value, regex, errorMessage, ...deps])
return result;
}
查找起来有点棘手,但错误的根源是将正则表达式作为
RegExp
传递。在每次渲染期间,都会创建
RegExp
对象的单独实例,从而导致重新渲染,在此期间创建另一个实例 - 依此类推。我通过将正则表达式作为字符串传递来解决这个问题:
function useRegexValidate(value: string, regex: string, errorMessage: string, deps: any[] = []): ValidationResult {
const [result, setResult] = useState<ValidationResult>(ValidationResult.Valid);
useEffect(() => {
let regexObj = new RegExp(regex);
if (!regexObj.test(value)) {
setResult(new ValidationResult(false, errorMessage));
} else {
setResult(ValidationResult.Valid);
}
}, [value, regex, errorMessage, ...deps])
return result;
}
我想我也可以通过不就地传递正则表达式来解决这个问题,而是创建一个常量值(因为表达式不太可能改变),例如
const regex: RegExp = /.../;
useRegexValidate(..., regex, ...);
但这会妨碍我的钩子的用户友好性,因为人们需要记住以特定的方式调用它,以免导致难以跟踪错误。所以这绝对不是一个选择。
我不太喜欢我选择的解决方案,因为每次执行验证时都需要创建 RegExp 对象。有没有办法以某种方式教 React 比较特定对象的内容而不仅仅是它们的引用?
有更好的方法来修复我的初始代码吗?
通常,当我有想要用作 deps 的对象时,我的解决方案是对它们进行 JSON.stringify。这样你就可以保持 RegExp 的类型安全。