121
if (arg !== undefined && arg.kind === 'Identifier') {
122
const setState = setStateFunctions.get(arg.identifier.id);
123
if (setState !== undefined) {
124
- errors.pushDiagnostic(
125
- CompilerDiagnostic.create({
126
- category: ErrorCategory.EffectSetState,
127
- reason:
128
- 'Calling setState synchronously within an effect can trigger cascading renders',
129
- description:
130
- 'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
131
- 'In general, the body of an effect should do one or both of the following:\n' +
132
- '* Update external systems with the latest state from React.\n' +
133
- '* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
134
- 'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
135
- '(https://react.dev/learn/you-might-not-need-an-effect)',
136
- suggestions: null,
137
- }).withDetails({
138
- kind: 'error',
139
- loc: setState.loc,
140
- message:
141
- 'Avoid calling setState() directly within an effect',
142
- }),
143
- );
124
+ const enableVerbose =
125
+ env.config.enableVerboseNoSetStateInEffect;
126
+ if (enableVerbose) {
127
+ errors.pushDiagnostic(
128
+ CompilerDiagnostic.create({
129
+ category: ErrorCategory.EffectSetState,
130
+ reason:
131
+ 'Calling setState synchronously within an effect can trigger cascading renders',
132
+ description:
133
+ 'Effects are intended to synchronize state between React and external systems. ' +
134
+ 'Calling setState synchronously causes cascading renders that hurt performance.\n\n' +
135
+ 'This pattern may indicate one of several issues:\n\n' +
136
+ '**1. Non-local derived data**: If the value being set could be computed from props/state ' +
137
+ 'but requires data from a parent component, consider restructuring state ownership so the ' +
138
+ 'derivation can happen during render in the component that owns the relevant state.\n\n' +
139
+ "**2. Derived event pattern**: If you're detecting when a prop changes (e.g., `isPlaying` " +
140
+ 'transitioning from false to true), this often indicates the parent should provide an event ' +
141
+ 'callback (like `onPlay`) instead of just the current state. Request access to the original event.\n\n' +
142
+ "**3. Force update / external sync**: If you're forcing a re-render to sync with an external " +
143
+ 'data source (mutable values outside React), use `useSyncExternalStore` to properly subscribe ' +
144
+ 'to external state changes.\n\n' +
145
+ 'See: https://react.dev/learn/you-might-not-need-an-effect',
146
+ suggestions: null,
147
+ }).withDetails({
148
+ kind: 'error',
149
+ loc: setState.loc,
150
+ message:
151
+ 'Avoid calling setState() directly within an effect',
152
+ }),
153
+ );
154
+ } else {
155
+ errors.pushDiagnostic(
156
+ CompilerDiagnostic.create({
157
+ category: ErrorCategory.EffectSetState,
158
+ reason:
159
+ 'Calling setState synchronously within an effect can trigger cascading renders',
160
+ description:
161
+ 'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
162
+ 'In general, the body of an effect should do one or both of the following:\n' +
163
+ '* Update external systems with the latest state from React.\n' +
164
+ '* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
165
+ 'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
166
+ '(https://react.dev/learn/you-might-not-need-an-effect)',
167
+ suggestions: null,
168
+ }).withDetails({
169
+ kind: 'error',
170
+ loc: setState.loc,
171
+ message:
172
+ 'Avoid calling setState() directly within an effect',
173
+ }),
174
+ );
175
+ }
176
}
177
}
178
}