|
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,
|
@@ -111,8 +111,10 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
|
111 | 111 | const modifiedContext = recordInstructionDerivations(instr, context);
|
112 | 112 |
|
113 | 113 | if (needsFixPointItr && modifiedContext) {
|
114 |
| - // If the derivation cache was modified, we need to re-run the fixpoint |
115 |
| - // iteration to ensure that all derived values are properly recorded. |
| 114 | + /* |
| 115 | + * If the derivation cache was modified, we need to re-run the fixpoint |
| 116 | + * iteration to ensure that all derived values are properly recorded. |
| 117 | + */ |
116 | 118 | recordPhiDerivations(block, context);
|
117 | 119 | }
|
118 | 120 | }
|
@@ -140,8 +142,10 @@ function recordPhiDerivations(
|
140 | 142 | operand.identifier.id,
|
141 | 143 | );
|
142 | 144 |
|
143 |
| - // if we don't have metadata for the operand, we need to re-run the fixpoint |
144 |
| - // if a future instruction modifies the derivation cache |
| 145 | + /* |
| 146 | + * if we don't have metadata for the operand, we need to re-run the fixpoint |
| 147 | + * if a future instruction modifies the derivation cache |
| 148 | + */ |
145 | 149 | if (operandMetadata === undefined) {
|
146 | 150 | needsFixPointItr = true;
|
147 | 151 | continue;
|
@@ -343,6 +347,7 @@ function validateEffect(
|
343 | 347 | value: CallExpression;
|
344 | 348 | loc: SourceLocation;
|
345 | 349 | sourceIds: Set<IdentifierId>;
|
| 350 | + typeOfValue: TypeOfValue; |
346 | 351 | }> = [];
|
347 | 352 |
|
348 | 353 | const globals: Set<IdentifierId> = new Set();
|
@@ -388,6 +393,7 @@ function validateEffect(
|
388 | 393 | value: instr.value,
|
389 | 394 | loc: instr.value.callee.loc,
|
390 | 395 | sourceIds: argMetadata.sourcesIds,
|
| 396 | + typeOfValue: argMetadata.typeOfValue, |
391 | 397 | });
|
392 | 398 | }
|
393 | 399 | } else if (instr.value.kind === 'CallExpression') {
|
@@ -429,14 +435,36 @@ function validateEffect(
|
429 | 435 | .length -
|
430 | 436 | 1
|
431 | 437 | ) {
|
432 |
| - context.errors.push({ |
433 |
| - category: ErrorCategory.EffectDerivationsOfState, |
434 |
| - reason: |
435 |
| - '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)', |
436 |
| - description: null, |
437 |
| - loc: derivedSetStateCall.value.callee.loc, |
438 |
| - suggestions: null, |
439 |
| - }); |
| 438 | + const derivedDepsStr = Array.from(derivedSetStateCall.sourceIds) |
| 439 | + .map(sourceId => { |
| 440 | + const sourceMetadata = context.derivationCache.get(sourceId); |
| 441 | + return sourceMetadata?.place.identifier.name?.value; |
| 442 | + }) |
| 443 | + .filter(Boolean) |
| 444 | + .join(', '); |
| 445 | + |
| 446 | + let description; |
| 447 | + |
| 448 | + if (derivedSetStateCall.typeOfValue === 'fromProps') { |
| 449 | + description = `From props: [${derivedDepsStr}]`; |
| 450 | + } else if (derivedSetStateCall.typeOfValue === 'fromState') { |
| 451 | + description = `From local state: [${derivedDepsStr}]`; |
| 452 | + } else { |
| 453 | + description = `From props and local state: [${derivedDepsStr}]`; |
| 454 | + } |
| 455 | + |
| 456 | + context.errors.pushDiagnostic( |
| 457 | + CompilerDiagnostic.create({ |
| 458 | + 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`, |
| 459 | + category: ErrorCategory.EffectDerivationsOfState, |
| 460 | + reason: |
| 461 | + 'You might not need an effect. Derive values in render, not effects.', |
| 462 | + }).withDetails({ |
| 463 | + kind: 'error', |
| 464 | + loc: derivedSetStateCall.value.callee.loc, |
| 465 | + message: 'This should be computed during render, not in an effect', |
| 466 | + }), |
| 467 | + ); |
440 | 468 | }
|
441 | 469 | }
|
442 | 470 | }
|
0 commit comments