Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be41c94e5c | ||
|
|
6891dcb87d | ||
|
|
3f40eb73a8 | ||
|
|
1d7e942da7 | ||
|
|
79dc706498 | ||
|
|
85bbe39ef8 | ||
|
|
820af20971 | ||
|
|
9be531cd37 | ||
|
|
b1cbb482d5 | ||
|
|
9c9136b441 | ||
|
|
33a2bf78c4 | ||
|
|
5d7e8b90e2 | ||
|
|
71236c9409 | ||
|
|
7ee7571212 | ||
|
|
6b22f31f1a | ||
|
|
b5c1637109 | ||
|
|
c60eebffea | ||
|
|
5dd622eabe | ||
|
|
904989f044 | ||
|
|
ab2681af03 | ||
|
|
101b20b663 | ||
|
|
eaee5308cc | ||
|
|
4a58b63865 | ||
|
|
cc015840ef | ||
|
|
19baee813c | ||
|
|
2aa5f9d4e3 | ||
|
|
8c587a2a41 | ||
|
|
12483a119b | ||
|
|
b2c30493ce | ||
|
|
36c2bf5c3e | ||
|
|
190758e623 | ||
|
|
b1a6f03f8a | ||
|
|
142fd27bf6 | ||
|
|
7ca2d4cd2e | ||
|
|
99be14c883 | ||
|
|
5a04619f60 |
@@ -608,6 +608,7 @@ module.exports = {
|
||||
symbol: 'readonly',
|
||||
SyntheticEvent: 'readonly',
|
||||
SyntheticMouseEvent: 'readonly',
|
||||
SyntheticPointerEvent: 'readonly',
|
||||
Thenable: 'readonly',
|
||||
TimeoutID: 'readonly',
|
||||
WheelEventHandler: 'readonly',
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
## 19.1.1 (July 28, 2025)
|
||||
|
||||
### React
|
||||
* Fixed Owner Stacks to work with ES2015 function.name semantics ([#33680](https://github.com/facebook/react/pull/33680) by @hoxyq)
|
||||
|
||||
## 19.1.0 (March 28, 2025)
|
||||
|
||||
### Owner Stack
|
||||
|
||||
@@ -219,6 +219,7 @@ function compile(
|
||||
validateNoImpureFunctionsInRender: true,
|
||||
validateStaticComponents: true,
|
||||
validateNoFreezingKnownMutableFunctions: true,
|
||||
validateNoVoidUseMemo: true,
|
||||
}
|
||||
: {
|
||||
/* use defaults for compiler mode */
|
||||
|
||||
@@ -174,7 +174,7 @@ function runWithEnvironment(
|
||||
!env.config.disableMemoizationForDebugging &&
|
||||
!env.config.enableChangeDetectionForDebugging
|
||||
) {
|
||||
dropManualMemoization(hir);
|
||||
dropManualMemoization(hir).unwrap();
|
||||
log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ export function lower(
|
||||
const fallthrough = builder.reserve('block');
|
||||
const terminal: ReturnTerminal = {
|
||||
kind: 'return',
|
||||
returnVariant: 'Implicit',
|
||||
loc: GeneratedSource,
|
||||
value: lowerExpressionToTemporary(builder, body),
|
||||
id: makeInstructionId(0),
|
||||
@@ -219,6 +220,7 @@ export function lower(
|
||||
builder.terminate(
|
||||
{
|
||||
kind: 'return',
|
||||
returnVariant: 'Void',
|
||||
loc: GeneratedSource,
|
||||
value: lowerValueToTemporary(builder, {
|
||||
kind: 'Primitive',
|
||||
@@ -302,6 +304,7 @@ function lowerStatement(
|
||||
}
|
||||
const terminal: ReturnTerminal = {
|
||||
kind: 'return',
|
||||
returnVariant: 'Explicit',
|
||||
loc: stmt.node.loc ?? GeneratedSource,
|
||||
value,
|
||||
id: makeInstructionId(0),
|
||||
|
||||
@@ -608,7 +608,7 @@ export const EnvironmentConfigSchema = z.object({
|
||||
*
|
||||
* Here the variables `ref` and `myRef` will be typed as Refs.
|
||||
*/
|
||||
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(false),
|
||||
enableTreatRefLikeIdentifiersAsRefs: z.boolean().default(true),
|
||||
|
||||
/*
|
||||
* If specified a value, the compiler lowers any calls to `useContext` to use
|
||||
@@ -631,6 +631,17 @@ export const EnvironmentConfigSchema = z.object({
|
||||
* ```
|
||||
*/
|
||||
lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
|
||||
|
||||
/**
|
||||
* If enabled, will validate useMemos that don't return any values:
|
||||
*
|
||||
* Valid:
|
||||
* useMemo(() => foo, [foo]);
|
||||
* useMemo(() => { return foo }, [foo]);
|
||||
* Invalid:
|
||||
* useMemo(() => { ... }, [...]);
|
||||
*/
|
||||
validateNoVoidUseMemo: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export type EnvironmentConfig = z.infer<typeof EnvironmentConfigSchema>;
|
||||
|
||||
@@ -446,8 +446,20 @@ export type ThrowTerminal = {
|
||||
};
|
||||
export type Case = {test: Place | null; block: BlockId};
|
||||
|
||||
export type ReturnVariant = 'Void' | 'Implicit' | 'Explicit';
|
||||
export type ReturnTerminal = {
|
||||
kind: 'return';
|
||||
/**
|
||||
* Void:
|
||||
* () => { ... }
|
||||
* function() { ... }
|
||||
* Implicit (ArrowFunctionExpression only):
|
||||
* () => foo
|
||||
* Explicit:
|
||||
* () => { return ... }
|
||||
* function () { return ... }
|
||||
*/
|
||||
returnVariant: ReturnVariant;
|
||||
loc: SourceLocation;
|
||||
value: Place;
|
||||
id: InstructionId;
|
||||
|
||||
@@ -1211,6 +1211,8 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [
|
||||
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
|
||||
]);
|
||||
|
||||
addObject(BUILTIN_SHAPES, ReanimatedSharedValueId, []);
|
||||
|
||||
addFunction(
|
||||
BUILTIN_SHAPES,
|
||||
[],
|
||||
|
||||
@@ -215,7 +215,7 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
|
||||
break;
|
||||
}
|
||||
case 'return': {
|
||||
value = `[${terminal.id}] Return${
|
||||
value = `[${terminal.id}] Return ${terminal.returnVariant}${
|
||||
terminal.value != null ? ' ' + printPlace(terminal.value) : ''
|
||||
}`;
|
||||
if (terminal.effects != null) {
|
||||
|
||||
@@ -777,6 +777,7 @@ export function mapTerminalSuccessors(
|
||||
case 'return': {
|
||||
return {
|
||||
kind: 'return',
|
||||
returnVariant: terminal.returnVariant,
|
||||
loc: terminal.loc,
|
||||
value: terminal.value,
|
||||
id: makeInstructionId(0),
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, SourceLocation} from '..';
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
SourceLocation,
|
||||
} from '..';
|
||||
import {
|
||||
CallExpression,
|
||||
Effect,
|
||||
@@ -30,6 +35,7 @@ import {
|
||||
makeInstructionId,
|
||||
} from '../HIR';
|
||||
import {createTemporaryPlace, markInstructionIds} from '../HIR/HIRBuilder';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
type ManualMemoCallee = {
|
||||
kind: 'useMemo' | 'useCallback';
|
||||
@@ -283,26 +289,43 @@ function extractManualMemoizationArgs(
|
||||
instr: TInstruction<CallExpression> | TInstruction<MethodCall>,
|
||||
kind: 'useCallback' | 'useMemo',
|
||||
sidemap: IdentifierSidemap,
|
||||
errors: CompilerError,
|
||||
): {
|
||||
fnPlace: Place;
|
||||
fnPlace: Place | null;
|
||||
depsList: Array<ManualMemoDependency> | null;
|
||||
} {
|
||||
const [fnPlace, depsListPlace] = instr.value.args as Array<
|
||||
Place | SpreadPattern | undefined
|
||||
>;
|
||||
if (fnPlace == null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Expected a callback function to be passed to ${kind}`,
|
||||
loc: instr.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected a callback function to be passed to ${kind}`,
|
||||
description: `Expected a callback function to be passed to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: `Expected a callback function to be passed to ${kind}`,
|
||||
}),
|
||||
);
|
||||
return {fnPlace: null, depsList: null};
|
||||
}
|
||||
if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Unexpected spread argument to ${kind}`,
|
||||
loc: instr.value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Unexpected spread argument to ${kind}`,
|
||||
description: `Unexpected spread argument to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: `Unexpected spread argument to ${kind}`,
|
||||
}),
|
||||
);
|
||||
return {fnPlace: null, depsList: null};
|
||||
}
|
||||
let depsList: Array<ManualMemoDependency> | null = null;
|
||||
if (depsListPlace != null) {
|
||||
@@ -310,23 +333,40 @@ function extractManualMemoizationArgs(
|
||||
depsListPlace.identifier.id,
|
||||
);
|
||||
if (maybeDepsList == null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
suggestions: null,
|
||||
loc: depsListPlace.loc,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
description: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: depsListPlace.loc,
|
||||
message: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
}),
|
||||
);
|
||||
return {fnPlace, depsList: null};
|
||||
}
|
||||
depsList = maybeDepsList.map(dep => {
|
||||
depsList = [];
|
||||
for (const dep of maybeDepsList) {
|
||||
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
||||
if (maybeDep == null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
suggestions: null,
|
||||
loc: dep.loc,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: dep.loc,
|
||||
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
depsList.push(maybeDep);
|
||||
}
|
||||
return maybeDep;
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
fnPlace,
|
||||
@@ -341,8 +381,14 @@ function extractManualMemoizationArgs(
|
||||
* rely on type inference to find useMemo/useCallback invocations, and instead does basic tracking
|
||||
* of globals and property loads to find both direct calls as well as usage via the React namespace,
|
||||
* eg `React.useMemo()`.
|
||||
*
|
||||
* This pass also validates that useMemo callbacks return a value (not void), ensuring that useMemo
|
||||
* is only used for memoizing values and not for running arbitrary side effects.
|
||||
*/
|
||||
export function dropManualMemoization(func: HIRFunction): void {
|
||||
export function dropManualMemoization(
|
||||
func: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const errors = new CompilerError();
|
||||
const isValidationEnabled =
|
||||
func.env.config.validatePreserveExistingMemoizationGuarantees ||
|
||||
func.env.config.validateNoSetStateInRender ||
|
||||
@@ -389,7 +435,47 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
instr as TInstruction<CallExpression> | TInstruction<MethodCall>,
|
||||
manualMemo.kind,
|
||||
sidemap,
|
||||
errors,
|
||||
);
|
||||
|
||||
if (fnPlace == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bailout on void return useMemos. This is an anti-pattern where code might be using
|
||||
* useMemo like useEffect: running arbirtary side-effects synced to changes in specific
|
||||
* values.
|
||||
*/
|
||||
if (
|
||||
func.env.config.validateNoVoidUseMemo &&
|
||||
manualMemo.kind === 'useMemo'
|
||||
) {
|
||||
const funcToCheck = sidemap.functions.get(
|
||||
fnPlace.identifier.id,
|
||||
)?.value;
|
||||
if (funcToCheck !== undefined && funcToCheck.loweredFunc.func) {
|
||||
if (!hasNonVoidReturn(funcToCheck.loweredFunc.func)) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'useMemo() callbacks must return a value',
|
||||
description: `This ${
|
||||
manualMemo.loadInstr.value.kind === 'PropertyLoad'
|
||||
? 'React.useMemo'
|
||||
: 'useMemo'
|
||||
} callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: 'useMemo() callbacks must return a value',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instr.value = getManualMemoizationReplacement(
|
||||
fnPlace,
|
||||
instr.value.loc,
|
||||
@@ -410,11 +496,19 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
* is rare and likely sketchy.
|
||||
*/
|
||||
if (!sidemap.functions.has(fnPlace.identifier.id)) {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason: `Expected the first argument to be an inline function expression`,
|
||||
suggestions: [],
|
||||
loc: fnPlace.loc,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the first argument to be an inline function expression`,
|
||||
description: `Expected the first argument to be an inline function expression`,
|
||||
suggestions: [],
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: fnPlace.loc,
|
||||
message: `Expected the first argument to be an inline function expression`,
|
||||
}),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const memoDecl: Place =
|
||||
manualMemo.kind === 'useMemo'
|
||||
@@ -486,6 +580,8 @@ export function dropManualMemoization(func: HIRFunction): void {
|
||||
markInstructionIds(func.body);
|
||||
}
|
||||
}
|
||||
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
function findOptionalPlaces(fn: HIRFunction): Set<IdentifierId> {
|
||||
@@ -530,3 +626,17 @@ function findOptionalPlaces(fn: HIRFunction): Set<IdentifierId> {
|
||||
}
|
||||
return optionals;
|
||||
}
|
||||
|
||||
function hasNonVoidReturn(func: HIRFunction): boolean {
|
||||
for (const [, block] of func.body.blocks) {
|
||||
if (block.terminal.kind === 'return') {
|
||||
if (
|
||||
block.terminal.returnVariant === 'Explicit' ||
|
||||
block.terminal.returnVariant === 'Implicit'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
isStableType,
|
||||
isStableTypeContainer,
|
||||
isUseOperator,
|
||||
isUseRefType,
|
||||
} from '../HIR';
|
||||
import {PostDominator} from '../HIR/Dominator';
|
||||
import {
|
||||
@@ -70,13 +69,6 @@ class StableSidemap {
|
||||
isStable: false,
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
this.env.config.enableTreatRefLikeIdentifiersAsRefs &&
|
||||
isUseRefType(lvalue.identifier)
|
||||
) {
|
||||
this.map.set(lvalue.identifier.id, {
|
||||
isStable: true,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ function emitSelectorFn(env: Environment, keys: Array<string>): Instruction {
|
||||
terminal: {
|
||||
id: makeInstructionId(0),
|
||||
kind: 'return',
|
||||
returnVariant: 'Explicit',
|
||||
loc: GeneratedSource,
|
||||
value: arrayInstr.lvalue,
|
||||
effects: null,
|
||||
|
||||
@@ -352,6 +352,7 @@ function emitOutlinedFn(
|
||||
terminal: {
|
||||
id: makeInstructionId(0),
|
||||
kind: 'return',
|
||||
returnVariant: 'Explicit',
|
||||
loc: GeneratedSource,
|
||||
value: instructions.at(-1)!.lvalue,
|
||||
effects: null,
|
||||
|
||||
@@ -451,6 +451,18 @@ function* generateInstructionTypes(
|
||||
|
||||
case 'JsxExpression':
|
||||
case 'JsxFragment': {
|
||||
if (env.config.enableTreatRefLikeIdentifiersAsRefs) {
|
||||
if (value.kind === 'JsxExpression') {
|
||||
for (const prop of value.props) {
|
||||
if (prop.kind === 'JsxAttribute' && prop.name === 'ref') {
|
||||
yield equation(prop.place.identifier.type, {
|
||||
kind: 'Object',
|
||||
shapeId: BuiltInUseRefId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yield equation(left, {kind: 'Object', shapeId: BuiltInJsxId});
|
||||
break;
|
||||
}
|
||||
@@ -466,7 +478,36 @@ function* generateInstructionTypes(
|
||||
yield equation(left, returnType);
|
||||
break;
|
||||
}
|
||||
case 'PropertyStore':
|
||||
case 'PropertyStore': {
|
||||
/**
|
||||
* Infer types based on assignments to known object properties
|
||||
* This is important for refs, where assignment to `<maybeRef>.current`
|
||||
* can help us infer that an object itself is a ref
|
||||
*/
|
||||
yield equation(
|
||||
/**
|
||||
* Our property type declarations are best-effort and we haven't tested
|
||||
* using them to drive inference of rvalues from lvalues. We want to emit
|
||||
* a Property type in order to infer refs from `.current` accesses, but
|
||||
* stay conservative by not otherwise inferring anything about rvalues.
|
||||
* So we use a dummy type here.
|
||||
*
|
||||
* TODO: consider using the rvalue type here
|
||||
*/
|
||||
makeType(),
|
||||
// unify() only handles properties in the second position
|
||||
{
|
||||
kind: 'Property',
|
||||
objectType: value.object.identifier.type,
|
||||
objectName: getName(names, value.object.identifier.id),
|
||||
propertyName: {
|
||||
kind: 'literal',
|
||||
value: value.property,
|
||||
},
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'DeclareLocal':
|
||||
case 'RegExpLiteral':
|
||||
case 'MetaProperty':
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {
|
||||
BlockId,
|
||||
HIRFunction,
|
||||
@@ -23,6 +27,7 @@ import {
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
* Validates that a function does not access a ref value during render. This includes a partial check
|
||||
@@ -258,12 +263,27 @@ function validateNoRefAccessInRenderImpl(
|
||||
env.set(place.identifier.id, type);
|
||||
}
|
||||
|
||||
const interpolatedAsJsx = new Set<IdentifierId>();
|
||||
for (const block of fn.body.blocks.values()) {
|
||||
for (const instr of block.instructions) {
|
||||
const {value} = instr;
|
||||
if (value.kind === 'JsxExpression' || value.kind === 'JsxFragment') {
|
||||
if (value.children != null) {
|
||||
for (const child of value.children) {
|
||||
interpolatedAsJsx.add(child.identifier.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; (i == 0 || env.hasChanged()) && i < 10; i++) {
|
||||
env.resetChanged();
|
||||
returnValues = [];
|
||||
const safeBlocks = new Map<BlockId, RefId>();
|
||||
const safeBlocks: Array<{block: BlockId; ref: RefId}> = [];
|
||||
const errors = new CompilerError();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
retainWhere(safeBlocks, entry => entry.block !== block.id);
|
||||
for (const phi of block.phis) {
|
||||
env.set(
|
||||
phi.place.identifier.id,
|
||||
@@ -385,28 +405,74 @@ function validateNoRefAccessInRenderImpl(
|
||||
const hookKind = getHookKindForType(fn.env, callee.identifier.type);
|
||||
let returnType: RefAccessType = {kind: 'None'};
|
||||
const fnType = env.get(callee.identifier.id);
|
||||
let didError = false;
|
||||
if (fnType?.kind === 'Structure' && fnType.fn !== null) {
|
||||
returnType = fnType.fn.returnType;
|
||||
if (fnType.fn.readRefEffect) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)',
|
||||
loc: callee.loc,
|
||||
description:
|
||||
callee.identifier.name !== null &&
|
||||
callee.identifier.name.kind === 'named'
|
||||
? `Function \`${callee.identifier.name.value}\` accesses a ref`
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
didError = true;
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: `This function accesses a ref value`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
if (hookKind != null) {
|
||||
validateNoDirectRefValueAccess(errors, operand, env);
|
||||
} else {
|
||||
validateNoRefAccess(errors, env, operand, operand.loc);
|
||||
/*
|
||||
* If we already reported an error on this instruction, don't report
|
||||
* duplicate errors
|
||||
*/
|
||||
if (!didError) {
|
||||
const isRefLValue = isUseRefType(instr.lvalue.identifier);
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
/**
|
||||
* By default we check that function call operands are not refs,
|
||||
* ref values, or functions that can access refs.
|
||||
*/
|
||||
if (isRefLValue || hookKind != null) {
|
||||
/**
|
||||
* Special cases:
|
||||
*
|
||||
* 1. the lvalue is a ref
|
||||
* In general passing a ref to a function may access that ref
|
||||
* value during render, so we disallow it.
|
||||
*
|
||||
* The main exception is the "mergeRefs" pattern, ie a function
|
||||
* that accepts multiple refs as arguments (or an array of refs)
|
||||
* and returns a new, aggregated ref. If the lvalue is a ref,
|
||||
* we assume that the user is doing this pattern and allow passing
|
||||
* refs.
|
||||
*
|
||||
* Eg `const mergedRef = mergeRefs(ref1, ref2)`
|
||||
*
|
||||
* 2. calling hooks
|
||||
*
|
||||
* Hooks are independently checked to ensure they don't access refs
|
||||
* during render.
|
||||
*/
|
||||
validateNoDirectRefValueAccess(errors, operand, env);
|
||||
} else if (interpolatedAsJsx.has(instr.lvalue.identifier.id)) {
|
||||
/**
|
||||
* Special case: the lvalue is passed as a jsx child
|
||||
*
|
||||
* For example `<Foo>{renderHelper(ref)}</Foo>`. Here we have more
|
||||
* context and infer that the ref is being passed to a component-like
|
||||
* render function which attempts to obey the rules.
|
||||
*/
|
||||
validateNoRefValueAccess(errors, env, operand);
|
||||
} else {
|
||||
validateNoRefPassedToFunction(
|
||||
errors,
|
||||
env,
|
||||
operand,
|
||||
operand.loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
env.set(instr.lvalue.identifier.id, returnType);
|
||||
@@ -439,17 +505,19 @@ function validateNoRefAccessInRenderImpl(
|
||||
case 'PropertyStore':
|
||||
case 'ComputedDelete':
|
||||
case 'ComputedStore': {
|
||||
const safe = safeBlocks.get(block.id);
|
||||
const target = env.get(instr.value.object.identifier.id);
|
||||
let safe: (typeof safeBlocks)['0'] | null | undefined = null;
|
||||
if (
|
||||
instr.value.kind === 'PropertyStore' &&
|
||||
safe != null &&
|
||||
target?.kind === 'Ref' &&
|
||||
target.refId === safe
|
||||
target != null &&
|
||||
target.kind === 'Ref'
|
||||
) {
|
||||
safeBlocks.delete(block.id);
|
||||
safe = safeBlocks.find(entry => entry.ref === target.refId);
|
||||
}
|
||||
if (safe != null) {
|
||||
retainWhere(safeBlocks, entry => entry !== safe);
|
||||
} else {
|
||||
validateNoRefAccess(errors, env, instr.value.object, instr.loc);
|
||||
validateNoRefUpdate(errors, env, instr.value.object, instr.loc);
|
||||
}
|
||||
for (const operand of eachInstructionValueOperand(instr.value)) {
|
||||
if (operand === instr.value.object) {
|
||||
@@ -535,8 +603,11 @@ function validateNoRefAccessInRenderImpl(
|
||||
|
||||
if (block.terminal.kind === 'if') {
|
||||
const test = env.get(block.terminal.test.identifier.id);
|
||||
if (test?.kind === 'Guard') {
|
||||
safeBlocks.set(block.terminal.consequent, test.refId);
|
||||
if (
|
||||
test?.kind === 'Guard' &&
|
||||
safeBlocks.find(entry => entry.ref === test.refId) == null
|
||||
) {
|
||||
safeBlocks.push({block: block.terminal.fallthrough, ref: test.refId});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,18 +654,17 @@ function destructure(
|
||||
|
||||
function guardCheck(errors: CompilerError, operand: Place, env: Env): void {
|
||||
if (env.get(operand.identifier.id)?.kind === 'Guard') {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
|
||||
loc: operand.loc,
|
||||
description:
|
||||
operand.identifier.name !== null &&
|
||||
operand.identifier.name.kind === 'named'
|
||||
? `Cannot access ref value \`${operand.identifier.name.value}\``
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,22 +678,21 @@ function validateNoRefValueAccess(
|
||||
type?.kind === 'RefValue' ||
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || operand.loc,
|
||||
description:
|
||||
operand.identifier.name !== null &&
|
||||
operand.identifier.name.kind === 'named'
|
||||
? `Cannot access ref value \`${operand.identifier.name.value}\``
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateNoRefAccess(
|
||||
function validateNoRefPassedToFunction(
|
||||
errors: CompilerError,
|
||||
env: Env,
|
||||
operand: Place,
|
||||
@@ -635,18 +704,43 @@ function validateNoRefAccess(
|
||||
type?.kind === 'RefValue' ||
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
description:
|
||||
operand.identifier.name !== null &&
|
||||
operand.identifier.name.kind === 'named'
|
||||
? `Cannot access ref value \`${operand.identifier.name.value}\``
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Passing a ref to a function may read its value during render`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateNoRefUpdate(
|
||||
errors: CompilerError,
|
||||
env: Env,
|
||||
operand: Place,
|
||||
loc: SourceLocation,
|
||||
): void {
|
||||
const type = destructure(env.get(operand.identifier.id));
|
||||
if (
|
||||
type?.kind === 'Ref' ||
|
||||
type?.kind === 'RefValue' ||
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Cannot update ref during render`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,17 +751,22 @@ function validateNoDirectRefValueAccess(
|
||||
): void {
|
||||
const type = destructure(env.get(operand.identifier.id));
|
||||
if (type?.kind === 'RefValue') {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
|
||||
loc: type.loc ?? operand.loc,
|
||||
description:
|
||||
operand.identifier.name !== null &&
|
||||
operand.identifier.name.kind === 'named'
|
||||
? `Cannot access ref value \`${operand.identifier.name.value}\``
|
||||
: null,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: type.loc ?? operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ERROR_DESCRIPTION =
|
||||
'React refs are values that are not needed for rendering. Refs should only be accessed ' +
|
||||
'outside of render, such as in event handlers or effects. ' +
|
||||
'Accessing a ref value (the `current` property) during render can cause your component ' +
|
||||
'not to update as expected (https://react.dev/reference/react/useRef)';
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component() {
|
||||
const ref = useRef(null);
|
||||
const ref2 = useRef(null);
|
||||
const mergedRef = mergeRefs([ref], ref2);
|
||||
|
||||
return <Stringify ref={mergedRef} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
const ref = useRef(null);
|
||||
const ref2 = useRef(null);
|
||||
const mergedRef = mergeRefs([ref], ref2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Stringify ref={mergedRef} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,11 @@
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component() {
|
||||
const ref = useRef(null);
|
||||
const ref2 = useRef(null);
|
||||
const mergedRef = mergeRefs([ref], ref2);
|
||||
|
||||
return <Stringify ref={mergedRef} />;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
|
||||
return <Foo>{props.render({ref})}</Foo>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(3);
|
||||
const ref = useRef(null);
|
||||
|
||||
const T0 = Foo;
|
||||
const t0 = props.render({ ref });
|
||||
let t1;
|
||||
if ($[0] !== T0 || $[1] !== t0) {
|
||||
t1 = <T0>{t0}</T0>;
|
||||
$[0] = T0;
|
||||
$[1] = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,9 @@
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
|
||||
return <Foo>{props.render({ref})}</Foo>;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
|
||||
return <Foo>{props.render(ref)}</Foo>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
const ref = useRef(null);
|
||||
let t0;
|
||||
if ($[0] !== props.render) {
|
||||
t0 = props.render(ref);
|
||||
$[0] = props.render;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] !== t0) {
|
||||
t1 = <Foo>{t0}</Foo>;
|
||||
$[2] = t0;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,9 @@
|
||||
// @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
|
||||
return <Foo>{props.render(ref)}</Foo>;
|
||||
}
|
||||
@@ -27,6 +27,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -100,8 +101,10 @@ function Component() {
|
||||
return t6;
|
||||
}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -23,6 +23,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -86,8 +87,10 @@ function Component() {
|
||||
return t5;
|
||||
}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -25,6 +25,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
@@ -83,8 +84,10 @@ function Component() {
|
||||
}
|
||||
function _temp() {}
|
||||
|
||||
function Child(t0) {
|
||||
const { ref } = t0;
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ function Component() {
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
// This violates the rules of React, so we access the ref in a child
|
||||
// component
|
||||
return ref.current;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
// the logical means the ref write is in a different block
|
||||
// from the if consequent. this tests that the "safe" blocks
|
||||
// extend up to the if's fallthrough
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
return <Child ref={ref} />;
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Child ref={ref} />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
function Child({ ref }) {
|
||||
"use no memo";
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ value: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 42
|
||||
@@ -0,0 +1,24 @@
|
||||
// @validateRefAccessDuringRender
|
||||
|
||||
import {useRef} from 'react';
|
||||
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
if (ref.current == null) {
|
||||
// the logical means the ref write is in a different block
|
||||
// from the if consequent. this tests that the "safe" blocks
|
||||
// extend up to the if's fallthrough
|
||||
ref.current = props.unknownKey ?? props.value;
|
||||
}
|
||||
return <Child ref={ref} />;
|
||||
}
|
||||
|
||||
function Child({ref}) {
|
||||
'use no memo';
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{value: 42}],
|
||||
};
|
||||
@@ -32,48 +32,30 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 4 errors:
|
||||
Found 2 errors:
|
||||
|
||||
Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.capture-ref-for-mutation.ts:12:13
|
||||
10 | };
|
||||
11 | const moveLeft = {
|
||||
> 12 | handler: handleKey('left')(),
|
||||
| ^^^^^^^^^^^^^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^ This function accesses a ref value
|
||||
13 | };
|
||||
14 | const moveRight = {
|
||||
15 | handler: handleKey('right')(),
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
error.capture-ref-for-mutation.ts:12:13
|
||||
10 | };
|
||||
11 | const moveLeft = {
|
||||
> 12 | handler: handleKey('left')(),
|
||||
| ^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
13 | };
|
||||
14 | const moveRight = {
|
||||
15 | handler: handleKey('right')(),
|
||||
|
||||
Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.capture-ref-for-mutation.ts:15:13
|
||||
13 | };
|
||||
14 | const moveRight = {
|
||||
> 15 | handler: handleKey('right')(),
|
||||
| ^^^^^^^^^^^^^^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
16 | };
|
||||
17 | return [moveLeft, moveRight];
|
||||
18 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.capture-ref-for-mutation.ts:15:13
|
||||
13 | };
|
||||
14 | const moveRight = {
|
||||
> 15 | handler: handleKey('right')(),
|
||||
| ^^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^^ This function accesses a ref value
|
||||
16 | };
|
||||
17 | return [moveLeft, moveRight];
|
||||
18 | }
|
||||
|
||||
@@ -22,24 +22,28 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.hook-ref-value.ts:5:23
|
||||
3 | function Component(props) {
|
||||
4 | const ref = useRef();
|
||||
> 5 | useEffect(() => {}, [ref.current]);
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
6 | }
|
||||
7 |
|
||||
8 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.hook-ref-value.ts:5:23
|
||||
3 | function Component(props) {
|
||||
4 | const ref = useRef();
|
||||
> 5 | useEffect(() => {}, [ref.current]);
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
6 | }
|
||||
7 |
|
||||
8 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -17,13 +17,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-access-ref-during-render.ts:4:16
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | const value = ref.current;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
5 | return value;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
@@ -21,13 +21,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-aliased-ref-in-callback-invoked-during-render-.ts:9:33
|
||||
7 | return <Foo item={item} current={current} />;
|
||||
8 | };
|
||||
> 9 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
10 | }
|
||||
11 |
|
||||
```
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @flow @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
import {makeObject_Primitives} from 'shared-runtime';
|
||||
|
||||
component Example() {
|
||||
const fooRef = makeObject_Primitives();
|
||||
fooRef.current = true;
|
||||
|
||||
return <Stringify foo={fooRef} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
4 | component Example() {
|
||||
5 | const fooRef = makeObject_Primitives();
|
||||
> 6 | fooRef.current = true;
|
||||
| ^^^^^^^^^^^^^^ Cannot update ref during render
|
||||
7 |
|
||||
8 | return <Stringify foo={fooRef} />;
|
||||
9 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// @flow @enableTreatRefLikeIdentifiersAsRefs @validateRefAccessDuringRender
|
||||
import {makeObject_Primitives} from 'shared-runtime';
|
||||
|
||||
component Example() {
|
||||
const fooRef = makeObject_Primitives();
|
||||
fooRef.current = true;
|
||||
|
||||
return <Stringify foo={fooRef} />;
|
||||
}
|
||||
@@ -18,13 +18,15 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-disallow-mutating-ref-in-render.ts:4:2
|
||||
2 | function Component() {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | ref.current = false;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
5 |
|
||||
6 | return <button ref={ref} />;
|
||||
7 | }
|
||||
|
||||
@@ -21,26 +21,17 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
Found 1 error:
|
||||
|
||||
Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-disallow-mutating-refs-in-render-transitive.ts:9:2
|
||||
7 | };
|
||||
8 | const changeRef = setRef;
|
||||
> 9 | changeRef();
|
||||
| ^^^^^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
10 |
|
||||
11 | return <button ref={ref} />;
|
||||
12 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-disallow-mutating-refs-in-render-transitive.ts:9:2
|
||||
7 | };
|
||||
8 | const changeRef = setRef;
|
||||
> 9 | changeRef();
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ This function accesses a ref value
|
||||
10 |
|
||||
11 | return <button ref={ref} />;
|
||||
12 | }
|
||||
|
||||
@@ -17,13 +17,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-pass-ref-to-function.ts:4:16
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | const x = foo(ref);
|
||||
| ^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^ Passing a ref to a function may read its value during render
|
||||
5 | return x.current;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
@@ -16,13 +16,15 @@ function Component({ref}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-read-ref-prop-in-render-destructure.ts:3:16
|
||||
1 | // @validateRefAccessDuringRender @compilationMode:"infer"
|
||||
2 | function Component({ref}) {
|
||||
> 3 | const value = ref.current;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
4 | return <div>{value}</div>;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -16,13 +16,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-read-ref-prop-in-render-property-load.ts:3:16
|
||||
1 | // @validateRefAccessDuringRender @compilationMode:"infer"
|
||||
2 | function Component(props) {
|
||||
> 3 | const value = props.ref.current;
|
||||
| ^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
4 | return <div>{value}</div>;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -20,13 +20,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-ref-in-callback-invoked-during-render.ts:8:33
|
||||
6 | return <Foo item={item} current={current} />;
|
||||
7 | };
|
||||
> 8 | return <Items>{props.items.map(item => renderItem(item))}</Items>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
9 | }
|
||||
10 |
|
||||
```
|
||||
|
||||
@@ -16,13 +16,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-ref-value-as-props.ts:4:19
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | return <Foo ref={ref.current} />;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
@@ -17,24 +17,28 @@ function Component(props) {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-set-and-read-ref-during-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef(null);
|
||||
> 4 | ref.current = props.value;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
5 | return ref.current;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-set-and-read-ref-during-render.ts:5:9
|
||||
3 | const ref = useRef(null);
|
||||
4 | ref.current = props.value;
|
||||
> 5 | return ref.current;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot access ref value during render
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
@@ -17,24 +17,28 @@ function Component(props) {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-set-and-read-ref-nested-property-during-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
3 | const ref = useRef({inner: null});
|
||||
> 4 | ref.current.inner = props.value;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
5 | return ref.current.inner;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-set-and-read-ref-nested-property-during-render.ts:5:9
|
||||
3 | const ref = useRef({inner: null});
|
||||
4 | ref.current.inner = props.value;
|
||||
> 5 | return ref.current.inner;
|
||||
| ^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
@@ -24,24 +24,28 @@ function Foo({a}) {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
|
||||
8 | // however, this is an instance of accessing a ref during render and is disallowed
|
||||
9 | // under React's rules, so we reject this input
|
||||
> 10 | const x = {a, val: val.ref.current};
|
||||
| ^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
11 |
|
||||
12 | return <VideoList videos={x} />;
|
||||
13 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
|
||||
8 | // however, this is an instance of accessing a ref during render and is disallowed
|
||||
9 | // under React's rules, so we reject this input
|
||||
> 10 | const x = {a, val: val.ref.current};
|
||||
| ^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
11 |
|
||||
12 | return <VideoList videos={x} />;
|
||||
13 | }
|
||||
|
||||
@@ -19,13 +19,15 @@ function useHook({value}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-write-but-dont-read-ref-in-render.ts:5:2
|
||||
3 | const ref = useRef(null);
|
||||
4 | // Writing to a ref in render is against the rules:
|
||||
> 5 | ref.current = value;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
6 | // returning a ref is allowed, so this alone doesn't trigger an error:
|
||||
7 | return ref;
|
||||
8 | }
|
||||
|
||||
@@ -17,13 +17,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.invalid-write-ref-prop-in-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
3 | const ref = props.ref;
|
||||
> 4 | ref.current = true;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
5 | return <div>{value}</div>;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
@@ -27,22 +27,26 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
6 | component C() {
|
||||
7 | const r = useRef(DEFAULT_VALUE);
|
||||
> 8 | if (r.current == DEFAULT_VALUE) {
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Cannot access ref value during render
|
||||
9 | r.current = 1;
|
||||
10 | }
|
||||
11 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
7 | const r = useRef(DEFAULT_VALUE);
|
||||
8 | if (r.current == DEFAULT_VALUE) {
|
||||
> 9 | r.current = 1;
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Cannot update ref during render
|
||||
10 | }
|
||||
11 | }
|
||||
12 |
|
||||
|
||||
@@ -25,12 +25,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
5 | const r = useRef(null);
|
||||
6 | if (r.current == null) {
|
||||
> 7 | f(r);
|
||||
| ^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^ Passing a ref to a function may read its value during render
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -25,12 +25,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
5 | const r = useRef(null);
|
||||
6 | if (r.current == null) {
|
||||
> 7 | f(r.current);
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
8 | }
|
||||
9 | }
|
||||
10 |
|
||||
|
||||
@@ -26,12 +26,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
6 | if (r.current == null) {
|
||||
7 | r.current = 42;
|
||||
> 8 | r.current = 42;
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Cannot update ref during render
|
||||
9 | }
|
||||
10 | }
|
||||
11 |
|
||||
|
||||
@@ -26,24 +26,26 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
4 | component C() {
|
||||
5 | const r = useRef(null);
|
||||
> 6 | const guard = r.current == null;
|
||||
| ^^^^^^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot access ref value during render
|
||||
7 | if (guard) {
|
||||
8 | r.current = 1;
|
||||
9 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
Cannot access ref value `guard`.
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
5 | const r = useRef(null);
|
||||
6 | const guard = r.current == null;
|
||||
> 7 | if (guard) {
|
||||
| ^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^ Cannot access ref value during render
|
||||
8 | r.current = 1;
|
||||
9 | }
|
||||
10 | }
|
||||
|
||||
@@ -26,12 +26,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
6 | const r2 = useRef(null);
|
||||
7 | if (r.current == null) {
|
||||
> 8 | r2.current = 1;
|
||||
| ^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^ Cannot update ref during render
|
||||
9 | }
|
||||
10 | }
|
||||
11 |
|
||||
|
||||
@@ -26,12 +26,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
7 | r.current = 1;
|
||||
8 | }
|
||||
> 9 | f(r.current);
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
10 | }
|
||||
11 |
|
||||
12 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -26,12 +26,14 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
7 | r.current = 1;
|
||||
8 | }
|
||||
> 9 | r.current = 1;
|
||||
| ^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^ Cannot update ref during render
|
||||
10 | }
|
||||
11 |
|
||||
12 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -22,13 +22,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.ref-optional.ts:5:9
|
||||
3 | function Component(props) {
|
||||
4 | const ref = useRef();
|
||||
> 5 | return ref?.current;
|
||||
| ^^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^^ Cannot access ref value during render
|
||||
6 | }
|
||||
7 |
|
||||
8 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
|
||||
|
||||
function Foo(props, ref) {
|
||||
const value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
mutate(value);
|
||||
if (CONST_TRUE) {
|
||||
return <Stringify ref={identity(ref)} />;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, {current: 'fake-ref-object'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.repro-ref-mutable-range.ts:11:36
|
||||
9 | mutate(value);
|
||||
10 | if (CONST_TRUE) {
|
||||
> 11 | return <Stringify ref={identity(ref)} />;
|
||||
| ^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
12 | }
|
||||
13 | return value;
|
||||
14 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -33,13 +33,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.todo-useCallback-set-ref-nested-property-ref-modified-later-preserve-memoization.ts:14:2
|
||||
12 |
|
||||
13 | // The ref is modified later, extending its range and preventing memoization of onChange
|
||||
> 14 | ref.current.inner = null;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
15 |
|
||||
16 | return <input onChange={onChange} />;
|
||||
17 | }
|
||||
|
||||
@@ -34,26 +34,17 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
Found 1 error:
|
||||
|
||||
Error: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.useCallback-accesses-ref-mutated-later-via-function-preserve-memoization.ts:17:2
|
||||
15 | ref.current.inner = null;
|
||||
16 | };
|
||||
> 17 | reset();
|
||||
| ^^^^^ This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
18 |
|
||||
19 | return <input onChange={onChange} />;
|
||||
20 | }
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.useCallback-accesses-ref-mutated-later-via-function-preserve-memoization.ts:17:2
|
||||
15 | ref.current.inner = null;
|
||||
16 | };
|
||||
> 17 | reset();
|
||||
| ^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^ This function accesses a ref value
|
||||
18 |
|
||||
19 | return <input onChange={onChange} />;
|
||||
20 | }
|
||||
|
||||
@@ -32,13 +32,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.useCallback-set-ref-nested-property-dont-preserve-memoization.ts:13:2
|
||||
11 | });
|
||||
12 |
|
||||
> 13 | ref.current.inner = null;
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Cannot update ref during render
|
||||
14 |
|
||||
15 | return <input onChange={onChange} />;
|
||||
16 | }
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
console.log('computing');
|
||||
}, []);
|
||||
const value2 = React.useMemo(() => {
|
||||
console.log('computing');
|
||||
}, []);
|
||||
return (
|
||||
<div>
|
||||
{value}
|
||||
{value2}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
|
||||
Error: useMemo() callbacks must return a value
|
||||
|
||||
This useMemo callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.
|
||||
|
||||
error.useMemo-no-return-value.ts:3:16
|
||||
1 | // @validateNoVoidUseMemo
|
||||
2 | function Component() {
|
||||
> 3 | const value = useMemo(() => {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
> 4 | console.log('computing');
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 5 | }, []);
|
||||
| ^^^^^^^^^ useMemo() callbacks must return a value
|
||||
6 | const value2 = React.useMemo(() => {
|
||||
7 | console.log('computing');
|
||||
8 | }, []);
|
||||
|
||||
Error: useMemo() callbacks must return a value
|
||||
|
||||
This React.useMemo callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.
|
||||
|
||||
error.useMemo-no-return-value.ts:6:17
|
||||
4 | console.log('computing');
|
||||
5 | }, []);
|
||||
> 6 | const value2 = React.useMemo(() => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
> 7 | console.log('computing');
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 8 | }, []);
|
||||
| ^^^^^^^^^ useMemo() callbacks must return a value
|
||||
9 | return (
|
||||
10 | <div>
|
||||
11 | {value}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
console.log('computing');
|
||||
}, []);
|
||||
const value2 = React.useMemo(() => {
|
||||
console.log('computing');
|
||||
}, []);
|
||||
return (
|
||||
<div>
|
||||
{value}
|
||||
{value2}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -32,6 +32,8 @@ Found 1 error:
|
||||
|
||||
Error: Expected the dependency list for useMemo to be an array literal
|
||||
|
||||
Expected the dependency list for useMemo to be an array literal
|
||||
|
||||
error.useMemo-non-literal-depslist.ts:10:4
|
||||
8 | return text.toUpperCase();
|
||||
9 | },
|
||||
|
||||
@@ -22,13 +22,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.validate-mutate-ref-arg-in-render.ts:3:14
|
||||
1 | // @validateRefAccessDuringRender:true
|
||||
2 | function Foo(props, ref) {
|
||||
> 3 | console.log(ref.current);
|
||||
| ^^^^^^^^^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^^^^^^^^^ Passing a ref to a function may read its value during render
|
||||
4 | return <div>{props.bar}</div>;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying component props or hook arguments is not allowed. Consider using a local variable instead.","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -51,12 +51,12 @@ function RefsInEffects() {
|
||||
const ref = useRefHelper();
|
||||
const wrapped = useDeeperRefHelper();
|
||||
let t0;
|
||||
if ($[0] !== ref.current || $[1] !== wrapped.foo.current) {
|
||||
if ($[0] !== ref || $[1] !== wrapped.foo.current) {
|
||||
t0 = () => {
|
||||
print(ref.current);
|
||||
print(wrapped.foo.current);
|
||||
};
|
||||
$[0] = ref.current;
|
||||
$[0] = ref;
|
||||
$[1] = wrapped.foo.current;
|
||||
$[2] = t0;
|
||||
} else {
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"This value cannot be modified","description":"Modifying component props or hook arguments is not allowed. Consider using a local variable instead.","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"severity":"InvalidReact","category":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function useHook(maybeRef) {
|
||||
return useCallback(() => {
|
||||
return [maybeRef.current];
|
||||
}, [maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `maybeRef.current`, but the source dependencies were [maybeRef]. Differences in ref.current access.
|
||||
|
||||
error.maybe-invalid-useCallback-read-maybeRef.ts:5:21
|
||||
3 |
|
||||
4 | function useHook(maybeRef) {
|
||||
> 5 | return useCallback(() => {
|
||||
| ^^^^^^^
|
||||
> 6 | return [maybeRef.current];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 7 | }, [maybeRef]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
return useMemo(() => {
|
||||
return () => [maybeRef.current];
|
||||
}, [shouldRead, maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Memoization: Compilation skipped because existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `maybeRef.current`, but the source dependencies were [shouldRead, maybeRef]. Differences in ref.current access.
|
||||
|
||||
error.maybe-invalid-useMemo-read-maybeRef.ts:5:17
|
||||
3 |
|
||||
4 | function useHook(maybeRef, shouldRead) {
|
||||
> 5 | return useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 6 | return () => [maybeRef.current];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 7 | }, [shouldRead, maybeRef]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -25,13 +25,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
error.maybe-mutable-ref-not-preserved.ts:8:33
|
||||
6 | function useFoo() {
|
||||
7 | const r = useRef();
|
||||
> 8 | return useMemo(() => makeArray(r), []);
|
||||
| ^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^ Passing a ref to a function may read its value during render
|
||||
9 | }
|
||||
10 |
|
||||
11 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
@@ -21,12 +21,14 @@ component Component(disableLocalRef, ref) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
|
||||
5 | const localRef = useFooRef();
|
||||
6 | const mergedRef = useMemo(() => {
|
||||
> 7 | return disableLocalRef ? ref : identity(ref, localRef);
|
||||
| ^^^ Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)
|
||||
| ^^^ Passing a ref to a function may read its value during render
|
||||
8 | }, [disableLocalRef, ref, localRef]);
|
||||
9 | return <div ref={mergedRef} />;
|
||||
10 | }
|
||||
|
||||
@@ -24,6 +24,8 @@ Found 1 error:
|
||||
|
||||
Error: Expected the first argument to be an inline function expression
|
||||
|
||||
Expected the first argument to be an inline function expression
|
||||
|
||||
error.validate-useMemo-named-function.ts:9:20
|
||||
7 | // for now.
|
||||
8 | function Component(props) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useCallback} from 'react';
|
||||
|
||||
function useHook(maybeRef) {
|
||||
return useCallback(() => {
|
||||
return [maybeRef.current];
|
||||
}, [maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useCallback } from "react";
|
||||
|
||||
function useHook(maybeRef) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== maybeRef) {
|
||||
t0 = () => [maybeRef.current];
|
||||
$[0] = maybeRef;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validatePreserveExistingMemoizationGuarantees
|
||||
import {useMemo} from 'react';
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
return useMemo(() => {
|
||||
return () => [maybeRef.current];
|
||||
}, [shouldRead, maybeRef]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
|
||||
import { useMemo } from "react";
|
||||
|
||||
function useHook(maybeRef, shouldRead) {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] !== maybeRef) {
|
||||
t0 = () => [maybeRef.current];
|
||||
$[0] = maybeRef;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
```javascript
|
||||
// @enableCustomTypeDefinitionForReanimated
|
||||
import {useAnimatedProps} from 'react-native-reanimated';
|
||||
import {useAnimatedProps, useSharedValue} from 'react-native-reanimated';
|
||||
function Component() {
|
||||
const radius = useSharedValue(50);
|
||||
|
||||
@@ -39,7 +39,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @enableCustomTypeDefinitionForReanimated
|
||||
import { useAnimatedProps } from "react-native-reanimated";
|
||||
import { useAnimatedProps, useSharedValue } from "react-native-reanimated";
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
const radius = useSharedValue(50);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @enableCustomTypeDefinitionForReanimated
|
||||
import {useAnimatedProps} from 'react-native-reanimated';
|
||||
import {useAnimatedProps, useSharedValue} from 'react-native-reanimated';
|
||||
function Component() {
|
||||
const radius = useSharedValue(50);
|
||||
|
||||
|
||||
@@ -47,28 +47,32 @@ function useCustomRef() {
|
||||
function _temp() {}
|
||||
|
||||
function Foo() {
|
||||
const $ = _c(3);
|
||||
const $ = _c(4);
|
||||
const ref = useCustomRef();
|
||||
let t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[0] !== ref) {
|
||||
t0 = () => {
|
||||
ref.current?.click();
|
||||
};
|
||||
t1 = [];
|
||||
$[0] = t0;
|
||||
$[1] = t1;
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t1 = $[1];
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [];
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
useEffect(t0, t1);
|
||||
let t2;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <div>foo</div>;
|
||||
$[2] = t2;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
t2 = $[3];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function Foo() {
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
ref.current?.click();
|
||||
}, []);
|
||||
}, [ref]);
|
||||
|
||||
return <button onClick={onClick} />;
|
||||
}
|
||||
@@ -47,24 +47,26 @@ function useCustomRef() {
|
||||
function _temp() {}
|
||||
|
||||
function Foo() {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
const ref = useCustomRef();
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[0] !== ref) {
|
||||
t0 = () => {
|
||||
ref.current?.click();
|
||||
};
|
||||
$[0] = t0;
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const onClick = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[2] !== onClick) {
|
||||
t1 = <button onClick={onClick} />;
|
||||
$[1] = t1;
|
||||
$[2] = onClick;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ function Foo() {
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
ref.current?.click();
|
||||
}, []);
|
||||
}, [ref]);
|
||||
|
||||
return <button onClick={onClick} />;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function Foo() {
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
customRef.current?.click();
|
||||
}, []);
|
||||
}, [customRef]);
|
||||
|
||||
return <button onClick={onClick} />;
|
||||
}
|
||||
@@ -47,24 +47,26 @@ function useCustomRef() {
|
||||
function _temp() {}
|
||||
|
||||
function Foo() {
|
||||
const $ = _c(2);
|
||||
const $ = _c(4);
|
||||
const customRef = useCustomRef();
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[0] !== customRef) {
|
||||
t0 = () => {
|
||||
customRef.current?.click();
|
||||
};
|
||||
$[0] = t0;
|
||||
$[0] = customRef;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
const onClick = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
if ($[2] !== onClick) {
|
||||
t1 = <button onClick={onClick} />;
|
||||
$[1] = t1;
|
||||
$[2] = onClick;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ function Foo() {
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
customRef.current?.click();
|
||||
}, []);
|
||||
}, [customRef]);
|
||||
|
||||
return <button onClick={onClick} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
|
||||
|
||||
function Foo(props, ref) {
|
||||
const value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
return <Stringify ref={ref} />;
|
||||
}
|
||||
mutate(value);
|
||||
if (CONST_TRUE) {
|
||||
return <Stringify ref={identity(ref)} />;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, {current: 'fake-ref-object'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { Stringify, identity, mutate, CONST_TRUE } from "shared-runtime";
|
||||
|
||||
function Foo(props, ref) {
|
||||
const $ = _c(7);
|
||||
let t0;
|
||||
let value;
|
||||
if ($[0] !== ref) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
value = {};
|
||||
if (CONST_TRUE) {
|
||||
mutate(value);
|
||||
t0 = <Stringify ref={ref} />;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
mutate(value);
|
||||
}
|
||||
$[0] = ref;
|
||||
$[1] = t0;
|
||||
$[2] = value;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
value = $[2];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
}
|
||||
if (CONST_TRUE) {
|
||||
let t1;
|
||||
if ($[3] !== ref) {
|
||||
t1 = identity(ref);
|
||||
$[3] = ref;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[5] !== t1) {
|
||||
t2 = <Stringify ref={t1} />;
|
||||
$[5] = t1;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Foo,
|
||||
params: [{}, { current: "fake-ref-object" }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) <div>{"ref":{"current":"fake-ref-object"}}</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => computeValue(), []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = computeValue();
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const value = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <div>{value}</div>;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,5 @@
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => computeValue(), []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
return;
|
||||
}, []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>{undefined}</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,7 @@
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
return;
|
||||
}, []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
return null;
|
||||
}, []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div>{null}</div>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,7 @@
|
||||
// @validateNoVoidUseMemo
|
||||
function Component() {
|
||||
const value = useMemo(() => {
|
||||
return null;
|
||||
}, []);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoVoidUseMemo
|
||||
function Component({items}) {
|
||||
const value = useMemo(() => {
|
||||
for (let item of items) {
|
||||
if (item.match) return item;
|
||||
}
|
||||
return null;
|
||||
}, [items]);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @validateNoVoidUseMemo
|
||||
function Component(t0) {
|
||||
const $ = _c(2);
|
||||
const { items } = t0;
|
||||
let t1;
|
||||
bb0: {
|
||||
for (const item of items) {
|
||||
if (item.match) {
|
||||
t1 = item;
|
||||
break bb0;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = null;
|
||||
}
|
||||
const value = t1;
|
||||
let t2;
|
||||
if ($[0] !== value) {
|
||||
t2 = <div>{value}</div>;
|
||||
$[0] = value;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
@@ -0,0 +1,10 @@
|
||||
// @validateNoVoidUseMemo
|
||||
function Component({items}) {
|
||||
const value = useMemo(() => {
|
||||
for (let item of items) {
|
||||
if (item.match) return item;
|
||||
}
|
||||
return null;
|
||||
}, [items]);
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
@@ -108,6 +108,7 @@ const COMPILER_OPTIONS: Partial<PluginOptions> = {
|
||||
validateNoImpureFunctionsInRender: true,
|
||||
validateStaticComponents: true,
|
||||
validateNoFreezingKnownMutableFunctions: true,
|
||||
validateNoVoidUseMemo: true,
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -1324,6 +1324,34 @@ const allTests = {
|
||||
`,
|
||||
errors: [asyncComponentHookError('use')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App({p1, p2}) {
|
||||
try {
|
||||
use(p1);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
use(p2);
|
||||
return <div>App</div>;
|
||||
}
|
||||
`,
|
||||
errors: [tryCatchUseError('use')],
|
||||
},
|
||||
{
|
||||
code: normalizeIndent`
|
||||
function App({p1, p2}) {
|
||||
try {
|
||||
doSomething();
|
||||
} catch {
|
||||
use(p1);
|
||||
}
|
||||
use(p2);
|
||||
return <div>App</div>;
|
||||
}
|
||||
`,
|
||||
errors: [tryCatchUseError('use')],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1383,7 +1411,7 @@ if (__EXPERIMENTAL__) {
|
||||
const onEvent = useEffectEvent((text) => {
|
||||
console.log(text);
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
onEvent('Hello world');
|
||||
});
|
||||
@@ -1421,7 +1449,7 @@ if (__EXPERIMENTAL__) {
|
||||
});
|
||||
return <Child onClick={() => onClick()} />
|
||||
}
|
||||
|
||||
|
||||
// The useEffectEvent function shares an identifier name with the above
|
||||
function MyLastComponent({theme}) {
|
||||
const onClick = useEffectEvent(() => {
|
||||
@@ -1573,6 +1601,12 @@ function asyncComponentHookError(fn) {
|
||||
};
|
||||
}
|
||||
|
||||
function tryCatchUseError(fn) {
|
||||
return {
|
||||
message: `React Hook "${fn}" cannot be called in a try/catch block.`,
|
||||
};
|
||||
}
|
||||
|
||||
// For easier local testing
|
||||
if (!process.env.CI) {
|
||||
let only = [];
|
||||
|
||||
@@ -247,9 +247,10 @@ const tests: CompilerTestCases = {
|
||||
name: 'Pipeline errors are reported',
|
||||
code: normalizeIndent`
|
||||
import useMyEffect from 'useMyEffect';
|
||||
import {AUTODEPS} from 'react';
|
||||
function Component({a}) {
|
||||
'use no memo';
|
||||
useMyEffect(() => console.log(a.b));
|
||||
useMyEffect(() => console.log(a.b), AUTODEPS);
|
||||
return <div>Hello world</div>;
|
||||
}
|
||||
`,
|
||||
@@ -262,7 +263,7 @@ const tests: CompilerTestCases = {
|
||||
source: 'useMyEffect',
|
||||
importSpecifierName: 'default',
|
||||
},
|
||||
numRequiredArgs: 1,
|
||||
autodepsIndex: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -110,6 +110,7 @@ const COMPILER_OPTIONS: Partial<PluginOptions> = {
|
||||
validateNoImpureFunctionsInRender: true,
|
||||
validateStaticComponents: true,
|
||||
validateNoFreezingKnownMutableFunctions: true,
|
||||
validateNoVoidUseMemo: true,
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
/* eslint-disable no-for-of-loops/no-for-of-loops */
|
||||
|
||||
import type {Rule, Scope} from 'eslint';
|
||||
import type {CallExpression, DoWhileStatement, Node} from 'estree';
|
||||
import type {
|
||||
CallExpression,
|
||||
CatchClause,
|
||||
DoWhileStatement,
|
||||
Node,
|
||||
TryStatement,
|
||||
} from 'estree';
|
||||
|
||||
// @ts-expect-error untyped module
|
||||
import CodePathAnalyzer from '../code-path-analysis/code-path-analyzer';
|
||||
@@ -111,6 +117,18 @@ function isInsideDoWhileLoop(node: Node | undefined): node is DoWhileStatement {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isInsideTryCatch(
|
||||
node: Node | undefined,
|
||||
): node is TryStatement | CatchClause {
|
||||
while (node) {
|
||||
if (node.type === 'TryStatement' || node.type === 'CatchClause') {
|
||||
return true;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isUseEffectEventIdentifier(node: Node): boolean {
|
||||
if (__EXPERIMENTAL__) {
|
||||
return node.type === 'Identifier' && node.name === 'useEffectEvent';
|
||||
@@ -532,6 +550,16 @@ const rule = {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Report an error if use() is called inside try/catch.
|
||||
if (isUseIdentifier(hook) && isInsideTryCatch(hook)) {
|
||||
context.report({
|
||||
node: hook,
|
||||
message: `React Hook "${getSourceCode().getText(
|
||||
hook,
|
||||
)}" cannot be called in a try/catch block.`,
|
||||
});
|
||||
}
|
||||
|
||||
// Report an error if a hook may be called more then once.
|
||||
// `use(...)` can be called in loops.
|
||||
if (
|
||||
|
||||
13
packages/react-client/src/ReactFlightClient.js
vendored
13
packages/react-client/src/ReactFlightClient.js
vendored
@@ -3647,7 +3647,7 @@ function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void {
|
||||
// $FlowFixMe[cannot-write]
|
||||
ioInfo.end += response._timeOrigin;
|
||||
|
||||
if (response._replayConsole) {
|
||||
if (enableComponentPerformanceTrack && response._replayConsole) {
|
||||
const env = response._rootEnvironmentName;
|
||||
const promise = ioInfo.value;
|
||||
if (promise) {
|
||||
@@ -4149,7 +4149,10 @@ function processFullStringRow(
|
||||
return;
|
||||
}
|
||||
case 78 /* "N" */: {
|
||||
if (enableProfilerTimer && enableComponentPerformanceTrack) {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
(enableComponentPerformanceTrack || enableAsyncDebugInfo)
|
||||
) {
|
||||
// Track the time origin for future debug info. We track it relative
|
||||
// to the current environment's time space.
|
||||
const timeOrigin: number = +row;
|
||||
@@ -4169,11 +4172,7 @@ function processFullStringRow(
|
||||
// Fallthrough to share the error with Console entries.
|
||||
}
|
||||
case 74 /* "J" */: {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableComponentPerformanceTrack &&
|
||||
enableAsyncDebugInfo
|
||||
) {
|
||||
if (enableProfilerTimer && enableAsyncDebugInfo) {
|
||||
resolveIOInfo(response, id, row);
|
||||
return;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user