Enable rules-of-hooks for DevTools (#34645)
Sebastian "Sebbie" Silbermann committed
Sep 29, 2025 at 15:31 UTC
24e260d35bb3a280aded6e79bc55080550569e28
10 files changed
+22
.eslintrc.js
+8
@@ -517,6 +517,14 @@ module.exports = {
517
__IS_INTERNAL_VERSION__: 'readonly',
518
},
519
},
520
+ {
521
+ files: ['packages/react-devtools-*/**/*.js'],
522
+ excludedFiles: '**/__tests__/**/*.js',
523
+ plugins: ['eslint-plugin-react-hooks-published'],
524
+ rules: {
525
+ 'react-hooks-published/rules-of-hooks': ERROR,
526
+ },
527
+ },
528
{
529
files: ['packages/eslint-plugin-react-hooks/src/**/*'],
530
extends: ['plugin:@typescript-eslint/recommended'],
package.json
+1
@@ -71,6 +71,7 @@
71
"eslint-plugin-no-for-of-loops": "^1.0.0",
72
"eslint-plugin-no-function-declare-after-return": "^1.0.0",
73
"eslint-plugin-react": "^6.7.1",
74
+ "eslint-plugin-react-hooks-published": "npm:eslint-plugin-react-hooks@^5.2.0",
75
"eslint-plugin-react-internal": "link:./scripts/eslint-rules",
76
"fbjs-scripts": "^3.0.1",
77
"filesize": "^6.0.1",
packages/react-devtools-shared/src/devtools/cache.js
+2
@@ -42,6 +42,7 @@ export type Resource<Input, Key, Value> = {
42
let readContext;
43
if (typeof React.use === 'function') {
44
readContext = function (Context: ReactContext<null>) {
45
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
46
return React.use(Context);
47
};
48
} else if (
@@ -141,6 +142,7 @@ export function createResource<Input, Key, Value>(
142
const key = hashInput(input);
143
const result: Thenable<Value> = accessResult(resource, fetch, input, key);
144
if (typeof React.use === 'function') {
145
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
146
return React.use(result);
147
}
148
packages/react-devtools-shared/src/devtools/views/ErrorBoundary/cache.js
+1
@@ -23,6 +23,7 @@ const API_TIMEOUT = 3000;
23
function readRecord<T>(record: Thenable<T>): T | null {
24
if (typeof React.use === 'function') {
25
try {
26
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
27
return React.use(record);
28
} catch (x) {
29
if (x === null) {
packages/react-devtools-shared/src/dynamicImportCache.js
+1
@@ -30,6 +30,7 @@ const moduleLoaderFunctionToModuleMap: Map<ModuleLoaderFunction, Module> =
30
function readRecord<T>(record: Thenable<T>): T | null {
31
if (typeof React.use === 'function') {
32
try {
33
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
34
return React.use(record);
35
} catch (x) {
36
if (x === null) {
packages/react-devtools-shared/src/hookNamesCache.js
+1
@@ -30,6 +30,7 @@ const TIMEOUT = 30000;
30
function readRecord<T>(record: Thenable<T>): T | null {
31
if (typeof React.use === 'function') {
32
try {
33
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
34
return React.use(record);
35
} catch (x) {
36
if (record.status === 'rejected') {
packages/react-devtools-shared/src/inspectedElementCache.js
+1
@@ -32,6 +32,7 @@ import type {
32
33
function readRecord<T>(record: Thenable<T>): T {
34
if (typeof React.use === 'function') {
35
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
36
return React.use(record);
37
}
38
if (record.status === 'fulfilled') {
packages/react-devtools-shell/src/app/SuspenseTree/index.js
+1
@@ -314,6 +314,7 @@ function LoadLater() {
314
315
function readRecord(promise: any): any {
316
if (typeof React.use === 'function') {
317
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
318
return React.use(promise);
319
}
320
switch (promise.status) {
packages/react-devtools-timeline/src/timelineCache.js
+1
@@ -29,6 +29,7 @@ const fileNameToProfilerDataMap: Map<
29
function readRecord<T>(record: Thenable<T>): T | Error {
30
if (typeof React.use === 'function') {
31
try {
32
+ // eslint-disable-next-line react-hooks-published/rules-of-hooks
33
return React.use(record);
34
} catch (x) {
35
if (record.status === 'rejected') {
yarn.lock
+5
@@ -8207,6 +8207,11 @@ eslint-plugin-no-unsanitized@4.0.2:
8207
resolved "https://registry.yarnpkg.com/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.0.2.tgz#e872b302cdfb5fe1262db989ba29cfcc334b499b"
8208
integrity sha512-Pry0S9YmHoz8NCEMRQh7N0Yexh2MYCNPIlrV52hTmS7qXnTghWsjXouF08bgsrrZqaW9tt1ZiK3j5NEmPE+EjQ==
8209
8210
+"eslint-plugin-react-hooks-published@npm:eslint-plugin-react-hooks@^5.2.0":
8211
+ version "5.2.0"
8212
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz#1be0080901e6ac31ce7971beed3d3ec0a423d9e3"
8213
+ integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==
8214
+
8215
"eslint-plugin-react-internal@link:./scripts/eslint-rules":
8216
version "0.0.0"
8217
uid ""