React Hook作为回调使用无ESLint警告的合规性及原因问询
这个问题的本质是静态校验工具的实现逻辑和Hook规则的设计初衷存在偏差,具体原因可以拆解为几点:
1. eslint校验规则是基于静态命名约定实现的
eslint-plugin-react-hooks的react-hooks/rules-of-hooks校验完全依赖静态语法匹配,不会分析代码的运行时逻辑:
- 只要函数名符合
use开头的驼峰命名规范,就会被识别为自定义Hook - 只要Hook调用是在首字母大写的React函数组件、或者
use开头的自定义Hook内部,就不会抛出“非法调用上下文”的警告
你把map的回调函数命名为useCb,刚好命中了自定义Hook的命名规则,eslint会认为useSomething是在合法的自定义Hook内部调用,自然不会触发警告。这不是校验逻辑的疏漏,静态检查工具本身就无法预判函数会在什么场景下被调用,只能基于社区约定的命名规则做判断。
2. 开发环境运行时没报错是因为你的用例刚好满足顺序一致要求
React在开发环境下的Hook运行时校验,只会检查单次组件渲染周期内的Hook调用总次数和顺序是否和上一次渲染一致。
你的示例中list是写死的静态数组,每次App组件渲染时map的循环次数固定,Hook的调用顺序、总数量都没有变化,所以不会触发运行时报错。
你可以做个测试:把list改成可动态修改的状态,比如加一个按钮往数组里新增元素,点击几次之后马上就会出现经典的Hook调用次数不匹配的运行时报错。
3. 这种写法本质上仍然违反Rules of Hooks
Rules of Hooks要求“只能在React函数顶层调用Hook”的核心目的,是保证每一次组件渲染时,Hook的调用顺序和总次数完全固定,和你是否把回调命名为use开头没有任何关系。
你当前的写法把Hook的调用次数和数组长度强绑定,只要数组长度动态变化,就会直接导致后续Hook的状态、副作用全部错乱,属于严重的生产环境bug隐患,是明确的违规写法。
4. 规则描述的歧义说明
你看到的“Hook必须在React函数组件或自定义Hook中调用”是简化版的规则描述,完整规则还包含了“必须在组件/自定义Hook的顶层调用,不能在循环、条件、回调中调用”的要求。静态校验工具做不到100%覆盖所有违规场景,才会出现你遇到的漏判情况。
内容的提问来源于stack exchange,提问作者kentr

