|
5 | 5 | * LICENSE file in the root directory of this source tree.
|
6 | 6 | */
|
7 | 7 |
|
8 |
| -import {CompilerError, Effect} from '..'; |
| 8 | +import {CompilerDiagnostic, CompilerError, Effect} from '..'; |
9 | 9 | import {ErrorCategory} from '../CompilerError';
|
10 | 10 | import {
|
11 | 11 | BlockId,
|
@@ -305,6 +305,7 @@ function validateEffect(
|
305 | 305 | value: CallExpression;
|
306 | 306 | loc: SourceLocation;
|
307 | 307 | sourceIds: Set<IdentifierId>;
|
| 308 | + typeOfValue: TypeOfValue; |
308 | 309 | }> = [];
|
309 | 310 |
|
310 | 311 | const globals: Set<IdentifierId> = new Set();
|
@@ -359,6 +360,7 @@ function validateEffect(
|
359 | 360 | value: instr.value,
|
360 | 361 | loc: instr.value.callee.loc,
|
361 | 362 | sourceIds: argMetadata.sourcesIds,
|
| 363 | + typeOfValue: argMetadata.typeOfValue, |
362 | 364 | });
|
363 | 365 | }
|
364 | 366 | } else if (instr.value.kind === 'CallExpression') {
|
@@ -398,14 +400,36 @@ function validateEffect(
|
398 | 400 | .length ===
|
399 | 401 | setStateCache.get(derivedSetStateCall.loc.identifierName)!.length
|
400 | 402 | ) {
|
401 |
| - errors.push({ |
402 |
| - category: ErrorCategory.EffectDerivationsOfState, |
403 |
| - reason: |
404 |
| - 'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)', |
405 |
| - description: null, |
406 |
| - loc: derivedSetStateCall.value.callee.loc, |
407 |
| - suggestions: null, |
408 |
| - }); |
| 403 | + const derivedDepsStr = Array.from(derivedSetStateCall.sourceIds) |
| 404 | + .map(sourceId => { |
| 405 | + const sourceMetadata = derivationCache.get(sourceId); |
| 406 | + return sourceMetadata?.place.identifier.name?.value; |
| 407 | + }) |
| 408 | + .filter(Boolean) |
| 409 | + .join(', '); |
| 410 | + |
| 411 | + let description; |
| 412 | + |
| 413 | + if (derivedSetStateCall.typeOfValue === 'fromProps') { |
| 414 | + description = `From props: [${derivedDepsStr}]`; |
| 415 | + } else if (derivedSetStateCall.typeOfValue === 'fromState') { |
| 416 | + description = `From local state: [${derivedDepsStr}]`; |
| 417 | + } else { |
| 418 | + description = `From props and local state: [${derivedDepsStr}]`; |
| 419 | + } |
| 420 | + |
| 421 | + errors.pushDiagnostic( |
| 422 | + CompilerDiagnostic.create({ |
| 423 | + description: `Derived values (${description}) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user`, |
| 424 | + category: ErrorCategory.EffectDerivationsOfState, |
| 425 | + reason: |
| 426 | + 'You might not need an effect. Derive values in render, not effects.', |
| 427 | + }).withDetails({ |
| 428 | + kind: 'error', |
| 429 | + loc: derivedSetStateCall.value.callee.loc, |
| 430 | + message: 'This should be computed during render, not in an effect', |
| 431 | + }), |
| 432 | + ); |
409 | 433 | }
|
410 | 434 | }
|
411 | 435 | }
|
0 commit comments