mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[compiler] Refactor validations to return Result and log where appropriate
Updates ~all of our validations to return a Result, and then updates callers to either unwrap() if they should bailout or else just log. ghstack-source-id: 418b5f5aa2b7dd49ca76b3f98a48a35150691d7e Pull Request resolved: https://github.com/facebook/react/pull/32688
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import type {SourceLocation} from './HIR';
|
||||
import {Err, Ok, Result} from './Utils/Result';
|
||||
import {assertExhaustive} from './Utils/utils';
|
||||
|
||||
export enum ErrorSeverity {
|
||||
@@ -224,6 +225,10 @@ export class CompilerError extends Error {
|
||||
return this.details.length > 0;
|
||||
}
|
||||
|
||||
asResult(): Result<void, CompilerError> {
|
||||
return this.hasErrors() ? Err(this) : Ok(undefined);
|
||||
}
|
||||
|
||||
/*
|
||||
* An error is critical if it means the compiler has entered into a broken state and cannot
|
||||
* continue safely. Other expected errors such as Todos mean that we can skip over that component
|
||||
|
||||
@@ -100,7 +100,7 @@ import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHI
|
||||
import {outlineJSX} from '../Optimization/OutlineJsx';
|
||||
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
|
||||
import {transformFire} from '../Transform';
|
||||
import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender';
|
||||
import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender';
|
||||
import {CompilerError} from '..';
|
||||
import {validateStaticComponents} from '../Validation/ValidateStaticComponents';
|
||||
|
||||
@@ -162,7 +162,7 @@ function runWithEnvironment(
|
||||
log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
|
||||
|
||||
validateContextVariableLValues(hir);
|
||||
validateUseMemo(hir);
|
||||
validateUseMemo(hir).unwrap();
|
||||
|
||||
if (
|
||||
env.isInferredMemoEnabled &&
|
||||
@@ -203,10 +203,10 @@ function runWithEnvironment(
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir);
|
||||
validateHooksUsage(hir).unwrap();
|
||||
}
|
||||
if (env.config.validateNoCapitalizedCalls) {
|
||||
validateNoCapitalizedCalls(hir);
|
||||
validateNoCapitalizedCalls(hir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,23 +256,23 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.validateRefAccessDuringRender) {
|
||||
validateNoRefAccessInRender(hir);
|
||||
validateNoRefAccessInRender(hir).unwrap();
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInRender) {
|
||||
validateNoSetStateInRender(hir);
|
||||
validateNoSetStateInRender(hir).unwrap();
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInPassiveEffects) {
|
||||
validateNoSetStateInPassiveEffects(hir);
|
||||
env.logErrors(validateNoSetStateInPassiveEffects(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoJSXInTryStatements) {
|
||||
validateNoJSXInTryStatement(hir);
|
||||
env.logErrors(validateNoJSXInTryStatement(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoImpureFunctionsInRender) {
|
||||
validateNoImpureFunctionsInRender(hir);
|
||||
validateNoImpureFunctionsInRender(hir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,14 +514,14 @@ function runWithEnvironment(
|
||||
});
|
||||
|
||||
if (env.config.validateMemoizedEffectDependencies) {
|
||||
validateMemoizedEffectDependencies(reactiveFunction);
|
||||
validateMemoizedEffectDependencies(reactiveFunction).unwrap();
|
||||
}
|
||||
|
||||
if (
|
||||
env.config.enablePreserveExistingMemoizationGuarantees ||
|
||||
env.config.validatePreserveExistingMemoizationGuarantees
|
||||
) {
|
||||
validatePreservedManualMemoization(reactiveFunction);
|
||||
validatePreservedManualMemoization(reactiveFunction).unwrap();
|
||||
}
|
||||
|
||||
const ast = codegenFunction(reactiveFunction, {
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Represents the possible kinds of value which may be stored at a given Place during
|
||||
@@ -87,7 +88,9 @@ function joinKinds(a: Kind, b: Kind): Kind {
|
||||
* may not appear as the callee of a conditional call.
|
||||
* See the note for Kind.PotentialHook for sources of potential hooks
|
||||
*/
|
||||
export function validateHooksUsage(fn: HIRFunction): void {
|
||||
export function validateHooksUsage(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const unconditionalBlocks = computeUnconditionalBlocks(fn);
|
||||
|
||||
const errors = new CompilerError();
|
||||
@@ -423,9 +426,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
|
||||
for (const [, error] of errorsByPlace) {
|
||||
errors.push(error);
|
||||
}
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
|
||||
|
||||
+5
-4
@@ -22,6 +22,7 @@ import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
} from '../ReactiveScopes/visitors';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates that all known effect dependencies are memoized. The algorithm checks two things:
|
||||
@@ -47,12 +48,12 @@ import {
|
||||
* mutate(object); // ... mutable range ends here after this mutation
|
||||
* ```
|
||||
*/
|
||||
export function validateMemoizedEffectDependencies(fn: ReactiveFunction): void {
|
||||
export function validateMemoizedEffectDependencies(
|
||||
fn: ReactiveFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const errors = new CompilerError();
|
||||
visitReactiveFunction(fn, new Visitor(), errors);
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
class Visitor extends ReactiveFunctionVisitor<CompilerError> {
|
||||
|
||||
+9
-3
@@ -4,11 +4,14 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import {CompilerError, EnvironmentConfig} from '..';
|
||||
import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
|
||||
import {HIRFunction, IdentifierId} from '../HIR';
|
||||
import {DEFAULT_GLOBALS} from '../HIR/Globals';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
export function validateNoCapitalizedCalls(fn: HIRFunction): void {
|
||||
export function validateNoCapitalizedCalls(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const envConfig: EnvironmentConfig = fn.env.config;
|
||||
const ALLOW_LIST = new Set([
|
||||
...DEFAULT_GLOBALS.keys(),
|
||||
@@ -26,6 +29,7 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
|
||||
);
|
||||
};
|
||||
|
||||
const errors = new CompilerError();
|
||||
const capitalLoadGlobals = new Map<IdentifierId, string>();
|
||||
const capitalizedProperties = new Map<IdentifierId, string>();
|
||||
const reason =
|
||||
@@ -73,7 +77,8 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
|
||||
const propertyIdentifier = value.property.identifier.id;
|
||||
const propertyName = capitalizedProperties.get(propertyIdentifier);
|
||||
if (propertyName != null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason,
|
||||
description: `${propertyName} may be a component.`,
|
||||
loc: value.loc,
|
||||
@@ -85,4 +90,5 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@
|
||||
import {CompilerError, ErrorSeverity} from '..';
|
||||
import {HIRFunction} from '../HIR';
|
||||
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Checks that known-impure functions are not called during render. Examples of invalid functions to
|
||||
@@ -18,7 +19,9 @@ import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
|
||||
* this in several of our validation passes and should unify those analyses into a reusable helper
|
||||
* and use it here.
|
||||
*/
|
||||
export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
|
||||
export function validateNoImpureFunctionsInRender(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const errors = new CompilerError();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const instr of block.instructions) {
|
||||
@@ -46,7 +49,5 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
+5
-4
@@ -7,6 +7,7 @@
|
||||
|
||||
import {CompilerError, ErrorSeverity} from '..';
|
||||
import {BlockId, HIRFunction} from '../HIR';
|
||||
import {Result} from '../Utils/Result';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
@@ -19,7 +20,9 @@ import {retainWhere} from '../Utils/utils';
|
||||
* created within a try block. JSX is allowed within a catch statement, unless that catch
|
||||
* is itself nested inside an outer try.
|
||||
*/
|
||||
export function validateNoJSXInTryStatement(fn: HIRFunction): void {
|
||||
export function validateNoJSXInTryStatement(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const activeTryBlocks: Array<BlockId> = [];
|
||||
const errors = new CompilerError();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
@@ -46,7 +49,5 @@ export function validateNoJSXInTryStatement(fn: HIRFunction): void {
|
||||
activeTryBlocks.push(block.terminal.handler);
|
||||
}
|
||||
}
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
+4
-2
@@ -99,9 +99,11 @@ class Env extends Map<IdentifierId, RefAccessType> {
|
||||
}
|
||||
}
|
||||
|
||||
export function validateNoRefAccessInRender(fn: HIRFunction): void {
|
||||
export function validateNoRefAccessInRender(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const env = new Env();
|
||||
validateNoRefAccessInRenderImpl(fn, env).unwrap();
|
||||
return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined);
|
||||
}
|
||||
|
||||
function refTypeOfType(place: Place): RefAccessType {
|
||||
|
||||
+5
-4
@@ -14,6 +14,7 @@ import {
|
||||
Place,
|
||||
} from '../HIR';
|
||||
import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates against calling setState in the body of a *passive* effect (useEffect),
|
||||
@@ -23,7 +24,9 @@ import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
* often bad for performance and frequently has more efficient and straightforward
|
||||
* alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples.
|
||||
*/
|
||||
export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
|
||||
export function validateNoSetStateInPassiveEffects(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const setStateFunctions: Map<IdentifierId, Place> = new Map();
|
||||
const errors = new CompilerError();
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
@@ -98,9 +101,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
function getSetStateCall(
|
||||
|
||||
+6
-8
@@ -9,7 +9,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
|
||||
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
|
||||
import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates that the given function does not have an infinite update loop
|
||||
@@ -39,9 +39,11 @@ import {Err, Ok, Result} from '../Utils/Result';
|
||||
* y();
|
||||
* ```
|
||||
*/
|
||||
export function validateNoSetStateInRender(fn: HIRFunction): void {
|
||||
export function validateNoSetStateInRender(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const unconditionalSetStateFunctions: Set<IdentifierId> = new Set();
|
||||
validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions).unwrap();
|
||||
return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions);
|
||||
}
|
||||
|
||||
function validateNoSetStateInRenderImpl(
|
||||
@@ -145,9 +147,5 @@ function validateNoSetStateInRenderImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
return Err(errors);
|
||||
} else {
|
||||
return Ok(undefined);
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
+7
-5
@@ -5,9 +5,10 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, Effect, ErrorSeverity} from '..';
|
||||
import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {
|
||||
DeclarationId,
|
||||
Effect,
|
||||
GeneratedSource,
|
||||
Identifier,
|
||||
IdentifierId,
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
} from '../ReactiveScopes/visitors';
|
||||
import {Result} from '../Utils/Result';
|
||||
import {getOrInsertDefault} from '../Utils/utils';
|
||||
|
||||
/**
|
||||
@@ -39,15 +41,15 @@ import {getOrInsertDefault} from '../Utils/utils';
|
||||
* This can occur if a value's mutable range somehow extended to include a hook and
|
||||
* was pruned.
|
||||
*/
|
||||
export function validatePreservedManualMemoization(fn: ReactiveFunction): void {
|
||||
export function validatePreservedManualMemoization(
|
||||
fn: ReactiveFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const state = {
|
||||
errors: new CompilerError(),
|
||||
manualMemoState: null,
|
||||
};
|
||||
visitReactiveFunction(fn, new Visitor(), state);
|
||||
if (state.errors.hasErrors()) {
|
||||
throw state.errors;
|
||||
}
|
||||
return state.errors.asResult();
|
||||
}
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
+2
-6
@@ -7,7 +7,7 @@
|
||||
|
||||
import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates against components that are created dynamically and whose identity is not guaranteed
|
||||
@@ -79,9 +79,5 @@ export function validateStaticComponents(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.hasErrors()) {
|
||||
return Err(error);
|
||||
} else {
|
||||
return Ok(undefined);
|
||||
}
|
||||
return error.asResult();
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError} from '..';
|
||||
import {CompilerError, ErrorSeverity} from '..';
|
||||
import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
export function validateUseMemo(fn: HIRFunction): void {
|
||||
export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
|
||||
const errors = new CompilerError();
|
||||
const useMemos = new Set<IdentifierId>();
|
||||
const react = new Set<IdentifierId>();
|
||||
const functions = new Map<IdentifierId, FunctionExpression>();
|
||||
@@ -61,7 +63,8 @@ export function validateUseMemo(fn: HIRFunction): void {
|
||||
}
|
||||
|
||||
if (body.loweredFunc.func.params.length > 0) {
|
||||
CompilerError.throwInvalidReact({
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'useMemo callbacks may not accept any arguments',
|
||||
description: null,
|
||||
loc: body.loc,
|
||||
@@ -70,7 +73,8 @@ export function validateUseMemo(fn: HIRFunction): void {
|
||||
}
|
||||
|
||||
if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
|
||||
CompilerError.throwInvalidReact({
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'useMemo callbacks may not be async or generator functions',
|
||||
description: null,
|
||||
@@ -84,4 +88,5 @@ export function validateUseMemo(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoJSXInTryStatements
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
let el;
|
||||
try {
|
||||
let value;
|
||||
try {
|
||||
value = identity(props.foo);
|
||||
} catch {
|
||||
el = <div value={value} />;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
9 | value = identity(props.foo);
|
||||
10 | } catch {
|
||||
> 11 | el = <div value={value} />;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (11:11)
|
||||
12 | }
|
||||
13 | } catch {
|
||||
14 | return null;
|
||||
```
|
||||
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoJSXInTryStatements
|
||||
function Component(props) {
|
||||
let el;
|
||||
try {
|
||||
el = <div />;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
3 | let el;
|
||||
4 | try {
|
||||
> 5 | el = <div />;
|
||||
| ^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (5:5)
|
||||
6 | } catch {
|
||||
7 | return null;
|
||||
8 | }
|
||||
```
|
||||
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
const [state, setState] = useState(0);
|
||||
const f = () => {
|
||||
setState(s => s + 1);
|
||||
};
|
||||
const g = () => {
|
||||
f();
|
||||
};
|
||||
useEffect(() => {
|
||||
g();
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
11 | };
|
||||
12 | useEffect(() => {
|
||||
> 13 | g();
|
||||
| ^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (13:13)
|
||||
14 | });
|
||||
15 | return state;
|
||||
16 | }
|
||||
```
|
||||
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
const [state, setState] = useState(0);
|
||||
useEffect(() => {
|
||||
setState(s => s + 1);
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Error
|
||||
|
||||
```
|
||||
5 | const [state, setState] = useState(0);
|
||||
6 | useEffect(() => {
|
||||
> 7 | setState(s => s + 1);
|
||||
| ^^^^^^^^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (7:7)
|
||||
8 | });
|
||||
9 | return state;
|
||||
10 | }
|
||||
```
|
||||
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @logger @validateNoJSXInTryStatements
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
let el;
|
||||
try {
|
||||
let value;
|
||||
try {
|
||||
value = identity(props.foo);
|
||||
} catch {
|
||||
el = <div value={value} />;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements
|
||||
import { identity } from "shared-runtime";
|
||||
|
||||
function Component(props) {
|
||||
const $ = _c(4);
|
||||
let el;
|
||||
try {
|
||||
let value;
|
||||
try {
|
||||
let t0;
|
||||
if ($[0] !== props.foo) {
|
||||
t0 = identity(props.foo);
|
||||
$[0] = props.foo;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
value = t0;
|
||||
} catch {
|
||||
let t0;
|
||||
if ($[2] !== value) {
|
||||
t0 = <div value={value} />;
|
||||
$[2] = value;
|
||||
$[3] = t0;
|
||||
} else {
|
||||
t0 = $[3];
|
||||
}
|
||||
el = t0;
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":11,"column":11,"index":214},"end":{"line":11,"column":32,"index":235},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"}}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":83},"end":{"line":17,"column":1,"index":290},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// @validateNoJSXInTryStatements
|
||||
// @logger @validateNoJSXInTryStatements
|
||||
import {identity} from 'shared-runtime';
|
||||
|
||||
function Component(props) {
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @logger @validateNoJSXInTryStatements
|
||||
function Component(props) {
|
||||
let el;
|
||||
try {
|
||||
el = <div />;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements
|
||||
function Component(props) {
|
||||
const $ = _c(1);
|
||||
let el;
|
||||
try {
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <div />;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
el = t0;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":5,"column":9,"index":96},"end":{"line":5,"column":16,"index":103},"filename":"invalid-jsx-in-try-with-catch.ts"}}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":41},"end":{"line":10,"column":1,"index":152},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// @validateNoJSXInTryStatements
|
||||
// @logger @validateNoJSXInTryStatements
|
||||
function Component(props) {
|
||||
let el;
|
||||
try {
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @logger @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
const [state, setState] = useState(0);
|
||||
const f = () => {
|
||||
setState(s => s + 1);
|
||||
};
|
||||
const g = () => {
|
||||
f();
|
||||
};
|
||||
useEffect(() => {
|
||||
g();
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(2);
|
||||
const [state, setState] = useState(0);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
const f = () => {
|
||||
setState(_temp);
|
||||
};
|
||||
|
||||
t0 = () => {
|
||||
f();
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const g = t0;
|
||||
let t1;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = () => {
|
||||
g();
|
||||
};
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
useEffect(t1);
|
||||
return state;
|
||||
}
|
||||
function _temp(s) {
|
||||
return s + 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":264},"end":{"line":13,"column":5,"index":265},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":16,"column":1,"index":292},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// @validateNoSetStateInPassiveEffects
|
||||
// @logger @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @logger @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
const [state, setState] = useState(0);
|
||||
useEffect(() => {
|
||||
setState(s => s + 1);
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Component() {
|
||||
const $ = _c(1);
|
||||
const [state, setState] = useState(0);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = () => {
|
||||
setState(_temp);
|
||||
};
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
useEffect(t0);
|
||||
return state;
|
||||
}
|
||||
function _temp(s) {
|
||||
return s + 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":179},"end":{"line":7,"column":12,"index":187},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":10,"column":1,"index":224},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: exception) Fixture not implemented
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// @validateNoSetStateInPassiveEffects
|
||||
// @logger @validateNoSetStateInPassiveEffects
|
||||
import {useEffect, useState} from 'react';
|
||||
|
||||
function Component() {
|
||||
Reference in New Issue
Block a user