eslint-plugin-react-hooks 7.1.0-canary-ec9cc003-20251208 → 7.1.0-canary-d763f313-20251210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18164,7 +18164,9 @@ function getRuleForCategoryImpl(category) {
|
|
|
18164
18164
|
category,
|
|
18165
18165
|
severity: ErrorSeverity.Error,
|
|
18166
18166
|
name: 'set-state-in-effect',
|
|
18167
|
-
description: 'Validates against calling setState synchronously in an effect
|
|
18167
|
+
description: 'Validates against calling setState synchronously in an effect. ' +
|
|
18168
|
+
'This can indicate non-local derived data, a derived event pattern, or ' +
|
|
18169
|
+
'improper external data synchronization.',
|
|
18168
18170
|
preset: LintRulePreset.Recommended,
|
|
18169
18171
|
};
|
|
18170
18172
|
}
|
|
@@ -32270,6 +32272,7 @@ const EnvironmentConfigSchema = v4.z.object({
|
|
|
32270
32272
|
validateNoVoidUseMemo: v4.z.boolean().default(true),
|
|
32271
32273
|
validateNoDynamicallyCreatedComponentsOrHooks: v4.z.boolean().default(false),
|
|
32272
32274
|
enableAllowSetStateFromRefsInEffects: v4.z.boolean().default(true),
|
|
32275
|
+
enableVerboseNoSetStateInEffect: v4.z.boolean().default(false),
|
|
32273
32276
|
enableInferEventHandlers: v4.z.boolean().default(false),
|
|
32274
32277
|
});
|
|
32275
32278
|
class Environment {
|
|
@@ -51245,21 +51248,48 @@ function validateNoSetStateInEffects(fn, env) {
|
|
|
51245
51248
|
if (arg !== undefined && arg.kind === 'Identifier') {
|
|
51246
51249
|
const setState = setStateFunctions.get(arg.identifier.id);
|
|
51247
51250
|
if (setState !== undefined) {
|
|
51248
|
-
|
|
51249
|
-
|
|
51250
|
-
|
|
51251
|
-
|
|
51252
|
-
'
|
|
51253
|
-
'
|
|
51254
|
-
|
|
51255
|
-
|
|
51256
|
-
|
|
51257
|
-
|
|
51258
|
-
|
|
51259
|
-
|
|
51260
|
-
|
|
51261
|
-
|
|
51262
|
-
|
|
51251
|
+
const enableVerbose = env.config.enableVerboseNoSetStateInEffect;
|
|
51252
|
+
if (enableVerbose) {
|
|
51253
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
51254
|
+
category: ErrorCategory.EffectSetState,
|
|
51255
|
+
reason: 'Calling setState synchronously within an effect can trigger cascading renders',
|
|
51256
|
+
description: 'Effects are intended to synchronize state between React and external systems. ' +
|
|
51257
|
+
'Calling setState synchronously causes cascading renders that hurt performance.\n\n' +
|
|
51258
|
+
'This pattern may indicate one of several issues:\n\n' +
|
|
51259
|
+
'**1. Non-local derived data**: If the value being set could be computed from props/state ' +
|
|
51260
|
+
'but requires data from a parent component, consider restructuring state ownership so the ' +
|
|
51261
|
+
'derivation can happen during render in the component that owns the relevant state.\n\n' +
|
|
51262
|
+
"**2. Derived event pattern**: If you're detecting when a prop changes (e.g., `isPlaying` " +
|
|
51263
|
+
'transitioning from false to true), this often indicates the parent should provide an event ' +
|
|
51264
|
+
'callback (like `onPlay`) instead of just the current state. Request access to the original event.\n\n' +
|
|
51265
|
+
"**3. Force update / external sync**: If you're forcing a re-render to sync with an external " +
|
|
51266
|
+
'data source (mutable values outside React), use `useSyncExternalStore` to properly subscribe ' +
|
|
51267
|
+
'to external state changes.\n\n' +
|
|
51268
|
+
'See: https://react.dev/learn/you-might-not-need-an-effect',
|
|
51269
|
+
suggestions: null,
|
|
51270
|
+
}).withDetails({
|
|
51271
|
+
kind: 'error',
|
|
51272
|
+
loc: setState.loc,
|
|
51273
|
+
message: 'Avoid calling setState() directly within an effect',
|
|
51274
|
+
}));
|
|
51275
|
+
}
|
|
51276
|
+
else {
|
|
51277
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
51278
|
+
category: ErrorCategory.EffectSetState,
|
|
51279
|
+
reason: 'Calling setState synchronously within an effect can trigger cascading renders',
|
|
51280
|
+
description: 'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
|
51281
|
+
'In general, the body of an effect should do one or both of the following:\n' +
|
|
51282
|
+
'* Update external systems with the latest state from React.\n' +
|
|
51283
|
+
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
|
51284
|
+
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
|
51285
|
+
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
|
51286
|
+
suggestions: null,
|
|
51287
|
+
}).withDetails({
|
|
51288
|
+
kind: 'error',
|
|
51289
|
+
loc: setState.loc,
|
|
51290
|
+
message: 'Avoid calling setState() directly within an effect',
|
|
51291
|
+
}));
|
|
51292
|
+
}
|
|
51263
51293
|
}
|
|
51264
51294
|
}
|
|
51265
51295
|
}
|
|
@@ -56278,6 +56308,8 @@ const COMPILER_OPTIONS = {
|
|
|
56278
56308
|
validateNoCapitalizedCalls: [],
|
|
56279
56309
|
validateHooksUsage: true,
|
|
56280
56310
|
validateNoDerivedComputationsInEffects: true,
|
|
56311
|
+
enableUseKeyedState: true,
|
|
56312
|
+
enableVerboseNoSetStateInEffect: true,
|
|
56281
56313
|
},
|
|
56282
56314
|
};
|
|
56283
56315
|
const FLOW_SUPPRESSION_REGEX = /\$FlowFixMe\[([^\]]*)\]/g;
|
|
@@ -18155,7 +18155,9 @@ function getRuleForCategoryImpl(category) {
|
|
|
18155
18155
|
category,
|
|
18156
18156
|
severity: ErrorSeverity.Error,
|
|
18157
18157
|
name: 'set-state-in-effect',
|
|
18158
|
-
description: 'Validates against calling setState synchronously in an effect
|
|
18158
|
+
description: 'Validates against calling setState synchronously in an effect. ' +
|
|
18159
|
+
'This can indicate non-local derived data, a derived event pattern, or ' +
|
|
18160
|
+
'improper external data synchronization.',
|
|
18159
18161
|
preset: LintRulePreset.Recommended,
|
|
18160
18162
|
};
|
|
18161
18163
|
}
|
|
@@ -32097,6 +32099,7 @@ const EnvironmentConfigSchema = v4.z.object({
|
|
|
32097
32099
|
validateNoVoidUseMemo: v4.z.boolean().default(true),
|
|
32098
32100
|
validateNoDynamicallyCreatedComponentsOrHooks: v4.z.boolean().default(false),
|
|
32099
32101
|
enableAllowSetStateFromRefsInEffects: v4.z.boolean().default(true),
|
|
32102
|
+
enableVerboseNoSetStateInEffect: v4.z.boolean().default(false),
|
|
32100
32103
|
enableInferEventHandlers: v4.z.boolean().default(false),
|
|
32101
32104
|
});
|
|
32102
32105
|
class Environment {
|
|
@@ -51072,21 +51075,48 @@ function validateNoSetStateInEffects(fn, env) {
|
|
|
51072
51075
|
if (arg !== undefined && arg.kind === 'Identifier') {
|
|
51073
51076
|
const setState = setStateFunctions.get(arg.identifier.id);
|
|
51074
51077
|
if (setState !== undefined) {
|
|
51075
|
-
|
|
51076
|
-
|
|
51077
|
-
|
|
51078
|
-
|
|
51079
|
-
'
|
|
51080
|
-
'
|
|
51081
|
-
|
|
51082
|
-
|
|
51083
|
-
|
|
51084
|
-
|
|
51085
|
-
|
|
51086
|
-
|
|
51087
|
-
|
|
51088
|
-
|
|
51089
|
-
|
|
51078
|
+
const enableVerbose = env.config.enableVerboseNoSetStateInEffect;
|
|
51079
|
+
if (enableVerbose) {
|
|
51080
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
51081
|
+
category: ErrorCategory.EffectSetState,
|
|
51082
|
+
reason: 'Calling setState synchronously within an effect can trigger cascading renders',
|
|
51083
|
+
description: 'Effects are intended to synchronize state between React and external systems. ' +
|
|
51084
|
+
'Calling setState synchronously causes cascading renders that hurt performance.\n\n' +
|
|
51085
|
+
'This pattern may indicate one of several issues:\n\n' +
|
|
51086
|
+
'**1. Non-local derived data**: If the value being set could be computed from props/state ' +
|
|
51087
|
+
'but requires data from a parent component, consider restructuring state ownership so the ' +
|
|
51088
|
+
'derivation can happen during render in the component that owns the relevant state.\n\n' +
|
|
51089
|
+
"**2. Derived event pattern**: If you're detecting when a prop changes (e.g., `isPlaying` " +
|
|
51090
|
+
'transitioning from false to true), this often indicates the parent should provide an event ' +
|
|
51091
|
+
'callback (like `onPlay`) instead of just the current state. Request access to the original event.\n\n' +
|
|
51092
|
+
"**3. Force update / external sync**: If you're forcing a re-render to sync with an external " +
|
|
51093
|
+
'data source (mutable values outside React), use `useSyncExternalStore` to properly subscribe ' +
|
|
51094
|
+
'to external state changes.\n\n' +
|
|
51095
|
+
'See: https://react.dev/learn/you-might-not-need-an-effect',
|
|
51096
|
+
suggestions: null,
|
|
51097
|
+
}).withDetails({
|
|
51098
|
+
kind: 'error',
|
|
51099
|
+
loc: setState.loc,
|
|
51100
|
+
message: 'Avoid calling setState() directly within an effect',
|
|
51101
|
+
}));
|
|
51102
|
+
}
|
|
51103
|
+
else {
|
|
51104
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
51105
|
+
category: ErrorCategory.EffectSetState,
|
|
51106
|
+
reason: 'Calling setState synchronously within an effect can trigger cascading renders',
|
|
51107
|
+
description: 'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
|
51108
|
+
'In general, the body of an effect should do one or both of the following:\n' +
|
|
51109
|
+
'* Update external systems with the latest state from React.\n' +
|
|
51110
|
+
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
|
51111
|
+
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
|
51112
|
+
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
|
51113
|
+
suggestions: null,
|
|
51114
|
+
}).withDetails({
|
|
51115
|
+
kind: 'error',
|
|
51116
|
+
loc: setState.loc,
|
|
51117
|
+
message: 'Avoid calling setState() directly within an effect',
|
|
51118
|
+
}));
|
|
51119
|
+
}
|
|
51090
51120
|
}
|
|
51091
51121
|
}
|
|
51092
51122
|
}
|
|
@@ -56105,6 +56135,8 @@ const COMPILER_OPTIONS = {
|
|
|
56105
56135
|
validateNoCapitalizedCalls: [],
|
|
56106
56136
|
validateHooksUsage: true,
|
|
56107
56137
|
validateNoDerivedComputationsInEffects: true,
|
|
56138
|
+
enableUseKeyedState: true,
|
|
56139
|
+
enableVerboseNoSetStateInEffect: true,
|
|
56108
56140
|
},
|
|
56109
56141
|
};
|
|
56110
56142
|
const FLOW_SUPPRESSION_REGEX = /\$FlowFixMe\[([^\]]*)\]/g;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks",
|
|
3
3
|
"description": "ESLint rules for React Hooks",
|
|
4
|
-
"version": "7.1.0-canary-
|
|
4
|
+
"version": "7.1.0-canary-d763f313-20251210",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/facebook/react.git",
|