☰
eslint-plugin-react 的 react-in-jsx-scope 规则详解:JSX 语法下的 React 作用域检查
2026/9/25 17:15:35 网站建设 项目流程
  • 开发工具
  • 代码质量
  • 静态分析

【免费下载链接】eslint-plugin-react

React-specific linting rules for ESLint

项目地址:https://gitcode.com/gh_mirrors/es/eslint-plugin-react
点击查看免费下载

react-in-jsx-scope是 eslint-plugin-react 中用于在 JSX 语法下检查React(或自定义 JSX pragma)变量是否处于作用域内的核心规则。它解决的是"写了 JSX 却忘了引入 React"这一在旧版 JSX 编译模型中极易出现的运行时错误。读完本文,你将掌握该规则的触发条件、正确的修复方式、@jsxpragma 的定制用法、与 React 17/19 新 JSX transform 的协作方式,以及它在仓库源码中的完整实现原理。

规则背景:为什么 JSX 需要 React 在作用域中

在 React 17 之前,JSX 只是一种语法糖:<a />在编译时会被展开为React.createElement("a")。这意味着无论代码中是否直接使用了React标识符,只要写了 JSX,React变量就必须存在于当前作用域中,否则编译产物会在运行时抛出ReferenceError: React is not defined。

react-in-jsx-scope规则正是针对这一约束设计的:它遍历 JSX 语法节点,检查 JSX 展开后所依赖的变量是否已经通过import、require或其他方式声明。规则的定义位于 lib/rules/react-in-jsx-scope.js,作者为 Glen Mailer,归属于Possible Errors(可能错误)类别,meta.docs.recommended为true。

规则详情:何时报错、何时放行

不正确的代码示例

场景一:使用了 JSX,但完全没有声明React变量:

var Hello = <div>Hello {this.props.name}</div>;

场景二:声明了React,但文件中的@jsx注解指定了其他 pragma(此时规则检查的是注解指定的变量,而非React):

/** @jsx Foo.bar */ var React = require('react'); var Hello = <div>Hello {this.props.name}</div>;

第二段代码报错的原因在于:@jsx Foo.bar告诉编译器用Foo.bar替代React.createElement,因此规则改为检查Foo是否在作用域中,而声明React是无济于事的。

正确的代码示例

方式一:ES Module 导入:

import React from 'react'; var Hello = <div>Hello {this.props.name}</div>;

方式二:CommonJS 引入:

var React = require('react'); var Hello = <div>Hello {this.props.name}</div>;

方式三:使用自定义 pragma 时,声明对应的变量:

/** @jsx Foo.bar */ var Foo = require('foo'); var Hello = <div>Hello {this.props.name}</div>;

可以看到,规则并不关心React/Foo是如何进入作用域的——import、require、var声明等任何一种方式都能通过检查。这与测试用例中'var React, App; <App />;'、'import React from react/addons; ...'等valid用例相互印证(见 tests/lib/rules/react-in-jsx-scope.js)。

报错信息

当检查失败时,规则通过report工具输出如下消息(模板定义于 lib/rules/react-in-jsx-scope.js):

'{{name}}' must be in scope when using JSX

其中{{name}}会被替换为当前生效的 pragma 名称(默认是React,自定义后是Foo等),以便开发者一眼定位缺失的变量。

如何配置与启用

通过recommended预设开启

该规则在recommended共享配置中是启用的,且严重级别为 error。其配置定义在 index.js:

'react/react-in-jsx-scope': SEVERITY_ERROR,

因此,在旧版 JSX 编译模式下,只需在你的 ESLint 配置中extends预设即可生效:

{ "extends": ["plugin:react/recommended"] }

使用 ESLint 9+ 的 flat config 时,可引入 flat 版本:

const reactRecommended = require('eslint-plugin-react/configs/recommended'); module.exports = [ { plugins: { react: require('eslint-plugin-react') }, ...reactRecommended, }, ];

flat 版本由 configs/recommended.js 提供,它复用了 legacy 预设的规则与parserOptions(ecmaFeatures.jsx: true),并通过Object.defineProperty将languageOptions设为不可枚举以兼容 ESLint 的配置合并逻辑。

通过jsx-runtime预设关闭

如果你使用了 React 17 引入的新 JSX transform(即react/jsx-runtime自动导入模式),编译器不再依赖React在作用域中,此时应当关闭本规则,同时关闭配套的 react/jsx-uses-react。仓库在 index.js 中内置了jsx-runtime预设,明确将这两条规则设为SEVERITY_OFF:

{ "extends": ["plugin:react/jsx-runtime"] }

flat config 对应版本为 configs/jsx-runtime.js。该预设还额外将parserOptions.jsxPragma置为null,以适配@typescript-eslint/parser的解析行为。

自定义 pragma:@jsx注解与settings.react.pragma

规则不仅检查React,还会尊重文件级@jsx注解以及 ESLint 共享设置中的settings.react.pragma。这一逻辑实现在 lib/util/pragma.js 的getFromContext中,其优先级如下:

  1. 读取源码中所有注释,用正则/@jsx\s+([^\s]+)/匹配第一个@jsx注解(lib/util/pragma.js);
  2. 若命中,取注解值(如Foo.bar)中以.分隔的第一段(即Foo)作为 pragma;
  3. 若没有注解,则回退读取context.settings.react.pragma;
  4. 若仍未配置,默认返回'React';
  5. 最后用JS_IDENTIFIER_REGEX(/^[_$a-zA-Z][_$a-zA-Z0-9]*$/)校验 pragma 是否为合法标识符,不合法时打印警告并回退为'React'。

也就是说,@jsx Foo.bar与@jsx Foo在作用域检查层面等价——都只要求Foo变量存在。测试用例中'/** @jsx Foo.Bar */ var Foo, App; <App />;'被判定为 valid,而'/** @jsx Foo.bar */ var React, a = <img />;'被判定为 invalid(报错变量名为Foo),正是对这一规则的直接验证(见 tests/lib/rules/react-in-jsx-scope.js 与 #L135-L143)。

若项目所有文件统一使用自定义 pragma,也可以在.eslintrc的settings中配置:

{ "settings": { "react": { "pragma": "Foo", "version": "18" } } }

此时规则会改为检查Foo。测试中'var Foo, App; <App />;'配上该settings即为 valid(见 tests/lib/rules/react-in-jsx-scope.js)。

源码实现:作用域查找与版本感知

监听节点与检查流程

规则主体在 lib/rules/react-in-jsx-scope.js,逻辑非常精简:create返回对两类 AST 节点的监听——JSXOpeningElement(如<App>)与JSXOpeningFragment(如<>),并对每个节点调用checkIfReactIsInScope。这意味着普通的 JSX 元素、自闭合标签以及 Fragment 简写形式都会被检查,测试中的'var a = <>fragment</>;'invalid 用例印证了 Fragment 也在检查范围内。

作用域链回溯

变量是否"在作用域中"由 lib/util/variable.js 的getVariableFromContext判定:它从当前节点的 scope 出发,先查找scope.variables,再逐层向上(scope = scope.upper)回溯,直到找到同名变量或耗尽整条作用域链。因此嵌套函数、块级作用域内使用 JSX 时,只要外层(如模块顶层)声明了React,同样能通过检查。

React 19:自动禁用

从源码中可以看出一条重要特性:create函数开头会调用testReactVersion(context, '>= 19.0.0'),若当前项目检测到 React 版本不低于 19.0.0,则直接返回空对象{},即该规则被自动禁用(lib/rules/react-in-jsx-scope.js)。原因在于 React 19 强制启用自动 JSX transform,React不再需要(也不允许依赖)在作用域中。测试用例中settings: { react: { version: '19.0.0' } }下的多段代码(包括完全没有声明React的情况)均被判定为 valid(见 tests/lib/rules/react-in-jsx-scope.js)。

版本比较基于 lib/util/version.js 的getReactVersionFromContext:它优先读取settings.react.version,支持'detect'值(通过resolve.sync('react')定位并读取已安装的 react 包版本),也支持从settings.react.defaultVersion读取兜底版本;未指定时使用999.999.999作为"最新版本"默认值。因此,即使你使用recommended预设,只要settings.react.version配置为'19'或'detect'且检测到 React 19,本规则也会静默失效。

何时不使用该规则

根据 docs/rules/react-in-jsx-scope.md 的说明,以下两类场景不应启用此规则:

  1. 项目不使用 JSX:没有 JSX 就没有React.createElement展开,检查毫无意义;
  2. 将React设为全局变量:例如在浏览器环境通过<script>直接引入全局React(或通过 ESLint 的globals声明React: true),此时无需在模块内声明即可通过作用域检查。

此外,使用 React 17 新 JSX transform 的项目应通过扩展plugin:react/jsx-runtime关闭本规则(同时关闭react/jsx-uses-react),以消除"导入未使用"的误报与冗余导入;而在 React 19 项目中,如前文所述,规则会被版本检测机制自动禁用,无需手动配置。

小结与最佳实践

react-in-jsx-scope是一条"编译模型驱动"的静态检查规则:它的存在意义来自旧版 JSX 的React.createElement展开机制。实际项目中的落地建议如下:

  • 旧版 JSX + Babel 传统 transform:沿用plugin:react/recommended预设,让规则以 error 级别拦截所有未引入React的 JSX 文件;
  • 自定义 pragma(如 Preact 的h、/** @jsx h */):通过settings.react.pragma或文件级注解定制,规则会自动切换到对应变量;
  • React 17+ 新 JSX transform:切换为plugin:react/jsx-runtime预设,与react/jsx-uses-react一并关闭;
  • React 19 项目:无需任何配置,版本检测会自动禁用本规则;
  • 全局React场景:在globals中声明React后关闭规则,避免误报。

如需深入阅读实现细节,可依次查阅规则源码 lib/rules/react-in-jsx-scope.js、pragm 解析工具 lib/util/pragma.js、作用域查找工具 lib/util/variable.js、版本检测工具 lib/util/version.js,以及覆盖全部合法/非法场景的测试套件 tests/lib/rules/react-in-jsx-scope.js。

  • 开发工具
  • 代码质量
  • 静态分析

【免费下载链接】eslint-plugin-react

React-specific linting rules for ESLint

项目地址:https://gitcode.com/gh_mirrors/es/eslint-plugin-react
点击查看免费下载
上一篇:Vant 组件库 useRelation 使用指南:基于 provide/inject 的父子组件通信方案
下一篇:Relay 20 刷新查询(Refreshing Queries)实战指南:useQueryLoader、useLazyLoadQuery 与 fetchQuery 三种刷新方案详解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询