Q: 如果我的eslintrc是这样配置的:
js
module.exports = {
extends: [
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended',
'@repo/eslint-config/strict',
'@repo/eslint-config/react',
'@repo/eslint-config/typescript',
'@repo/eslint-config/prettier',
],
rules: {
// 'react/jsx-uses-vars': 'error',
},
};
那么多个extends
的结果,是json
的merge
覆盖吗 还是相同的配置会被deep merge
?
Answer:
ESLint 在处理 extends
时,行为不是一个"深度 merge",而是有明确的合并/覆盖规则:
1. 规则(rules)的合并方式
-
各个
extends
里的rules
最终会合并成一个对象。 -
同一个规则 如果在多个配置里都出现,后面的会覆盖前面的(即后者优先级更高)。
-
合并是 浅层级的,不会做 deep merge。例如:
js// 第一个 extends { rules: { 'no-unused-vars': ['error', { vars: 'all' }] } } // 第二个 extends { rules: { 'no-unused-vars': ['warn'] } }
最终结果就是
{ 'no-unused-vars': ['warn'] }
,前一个的{ vars: 'all' }
整个丢掉。
2. 其他配置项的合并
parserOptions
、settings
等对象字段:同样是 浅合并,同名 key 后者覆盖前者。plugins
:会做合并去重(数组拼接,不会覆盖)。env
:多个配置里的布尔值会合并(true 会"叠加")。
3. 你的例子里
js
extends: [
'plugin:react/recommended',
'@yhdfe/eslint-config/strict',
'@yhdfe/eslint-config/react',
'@yhdfe/eslint-config/typescript',
'@yhdfe/eslint-config/prettier',
],
- 最终生效的配置是按顺序从前到后合并的。
- 如果
plugin:react/recommended
打开了react/jsx-uses-vars
,但你后面的某个包(例如@yhdfe/eslint-config/react
)把它关掉了,那最终就是"被关掉"。 - 如果你在
rules
里再手动写一遍,那就是 最终权威值(最高优先级)。
4. 怎么验证?
你可以直接运行:
arduino
npx eslint --print-config index.js
它会输出 index.js 最终生效的完整配置(合并后的结果),这样就能直观看到 react/jsx-uses-vars
是什么状态。
✅ 总结:
extends
的合并是 浅合并,同名规则或设置,后者覆盖前者。plugins
、env
这类会做合并,rules
、parserOptions
、settings
这类就是浅覆盖。- 想知道最终结果,用
--print-config
最靠谱。
Q:我有一个index.js文件里,有这样的代码:
js
import React from 'react';
import ReactDom from 'react-dom';
import ToastContainer from './ToastContainer';
const toastContainerDiv = document.createElement('div');
document.body.appendChild(toastContainerDiv);
const getToastContainerRef = () => {
return ReactDom.render(<ToastContainer />, toastContainerDiv);
};
看代码的话,ToastContainer这个变量有被调用,但是报了eslint error:'ToastContainer' is defined but never used.eslint(no-unused-vars)。
以下是我的eslint配置文件:
js
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
requireConfigFile: false,
babelOptions: {
presets: ['@babel/preset-react'],
},
},
plugins: ['react', 'react-hooks'],
settings: {
react: {
version: 'detect',
},
},
rules: {
// eslint built-in rules
"no-param-reassign": 2, // 禁止对函数参数进行重新赋值
"no-prototype-builtins": 0, // 允许使用Object.prototype的内置方法
// eslint-plugin-react规则
"react/display-name": 0, // 允许组件缺少displayName属性
"react/jsx-key": 1, // 警告缺少key属性的jsx元素
"react/no-array-index-key": 1, // 警告在jsx元素中使用数组索引作为key
"react/prop-types": 0, // 允许缺少propTypes属性
"react/react-in-jsx-scope": 0, // 允许在jsx中使用React变量而不导入React模块
"react/self-closing-comp": 1, // 警告没有子元素的自闭合组件
// eslint-plugin-react-hooks规则
"react-hooks/exhaustive-deps": 1, // 警告useEffect和useCallback的依赖项不完整
"react-hooks/rules-of-hooks": 2, // 强制遵守React Hooks的规则
// eslint-plugin-jsx-a11y规则
"jsx-a11y/accessible-emoji": "off", // 关闭使用emoji的警告
"jsx-a11y/click-events-have-key-events": 0, // 允许点击事件没有对应的键盘事件
"jsx-a11y/no-static-element-interactions": 0, // 允许静态HTML元素没有点击事件时不需要增加role属性
"jsx-a11y/anchor-is-valid": [
"warn",
{
components: ["Link"],
specialLink: ["hrefLeft", "hrefRight"],
aspects: ["invalidHref", "preferButton"],
},
], // 警告无效的锚点链接和推荐使用按钮代替的情况
"jsx-a11y/anchor-has-content": [
0,
{
components: ["Anchor"],
},
], // 允许锚点元素没有内容
},
};
分析一下为什么会有这个eslint的误报,要如何修复?
Answer:
好------我一步步把可能的原因和可行的修复都列清楚,结论先说在最前面:
结论(最可能) :ESLint 的核心规则 no-unused-vars
报 ToastContainer is defined but never used
,是因为 ESLint 默认不会把 JSX 中使用的组件名 当作"已使用的变量"来识别。eslint-plugin-react
提供了把 JSX 用法标记成"已使用变量"的规则(react/jsx-uses-vars
/ 以及历史上的 react/jsx-uses-react
),你当前的配置没有启用这类规则(或插件/版本未生效),所以才会误报。
下面按步骤分析与修复(包含可选方案、调试检查项和最小改动示例)。
一、为什么会发生(详细分析)
-
ESLint core 的
no-unused-vars
规则 只看"变量在 AST 中是否被引用",但旧版/纯核心并不会把 JSX 元素<ToastContainer />
的出现自动视为对ToastContainer
变量的引用。 -
eslint-plugin-react
提供了两个帮助类规则来解决这个问题:react/jsx-uses-react
------ 告诉 ESLintReact
(旧的 JSX runtime)在 JSX 中被使用(用于避免React
被误判为未使用)。react/jsx-uses-vars
------ 告诉 ESLint "以大写开头的 JSX 标识符在 JSX 中是被使用的变量",避免组件名被no-unused-vars
误判。
-
你的配置里虽然有
plugins: ['react', 'react-hooks']
且settings.react.version
也有detect
,但你并没有 显式启用react/jsx-uses-vars
,也没有extends: ['plugin:react/recommended']
之类去自动打开这些关联规则。因此 ESLint 仍然把ToastContainer
视作"定义但未使用"。
另外可能的次要原因(排查项):
eslint-plugin-react
没有真实安装(plugins
写了但依赖缺失) → 插件规则不会生效。- 使用了不兼容的插件/ESLint 版本(插件老或者 ESLint 太老),导致规则名不存在或被忽略。
- ESLint 缓存导致旧报错仍显示(
--no-cache
可排查)。
二、推荐的最小修复(优先推荐)
在你现在的 .eslintrc
的 rules
里加一条:
js
// 在现有 module.exports.rules 内添加
"react/jsx-uses-vars": "error",
完整示例片段(只展示关键位置):
js
module.exports = {
parser: '@babel/eslint-parser',
parserOptions: { /* ...你原来的配置... */ },
plugins: ['react', 'react-hooks'],
settings: { react: { version: 'detect' } },
rules: {
"react/jsx-uses-vars": "error",
// 你原来的规则...
"no-param-reassign": 2,
"no-prototype-builtins": 0,
// ...
},
};
这样做后 eslint
会把 <ToastContainer />
的使用识别为对 ToastContainer
的引用,从而消除 no-unused-vars
的误报。
三、其他可选方案(依据场景选择)
A. 使用插件的推荐配置(会带来更多 React 规则)
js
extends: [
'plugin:react/recommended'
],
优点:自动启用一批推荐规则(包含避免 JSX 误报的规则)。缺点:会启用很多规则,可能需要额外调整。
B. 如果你使用 React 17+ 的新 JSX 转换(automatic runtime)
eslint-plugin-react
提供 plugin:react/jsx-runtime
的 extend(取决于插件版本)。如果你使用新 transform,可以考虑加上它。但最简洁仍是单独打开 react/jsx-uses-vars
。
C. 文件级/行级忽略(不推荐,临时可用)
在 import 那行加注释:
js
import ToastContainer from './ToastContainer'; // eslint-disable-line no-unused-vars
只用于临时屏蔽报错,不推荐作为长期解决方案。
四、调试步骤(如果上面修复不生效,请按这个顺序检查)
- 确认
eslint-plugin-react
真安装:npm ls eslint-plugin-react
或pnpm ls eslint-plugin-react
。 - 清除 ESLint 缓存后重跑:
eslint --no-cache index.js
。 - 打印 eslint 生效的配置查看最终是否包含规则:
npx eslint --print-config index.js
,看输出中是否包含"react/jsx-uses-vars" : "error"
。 - 若 ESLint 或 plugin 版本较旧,考虑升级
eslint
和eslint-plugin-react
到较新的稳定版本(通常能避免兼容性问题)。
五、补充说明(容易混淆的点)
- 你把
react/react-in-jsx-scope
设为0
(允许 JSX 时不导入 React)这和ToastContainer
的误报无关。react-in-jsx-scope
只是控制React
是否必须在文件中声明。 react/jsx-uses-react
(标记React
被使用)跟react/jsx-uses-vars
(标记 JSX 元素名被使用)是两个不同作用的规则;这里我们关心后者。
六、快速结论与建议
- 最简单、最小改动 :在 ESLint 配置里加入
"react/jsx-uses-vars": "error"
,然后重跑 ESLint。 - 若你愿意接受更多 React 相关检查,改用
extends: ['plugin:react/recommended']
。 - 如果仍不生效,请确认
eslint-plugin-react
已安装且 ESLint 不是用缓存运行,并用--print-config
检查最终配置。