compiler/packages/babel-plugin-react-compiler/docs/passes/47-validateNoJSXInTryStatement.md MARKDOWN 168 lines View on github.com → Search inside
1# validateNoJSXInTryStatement23## File4`src/Validation/ValidateNoJSXInTryStatement.ts`56## Purpose7Validates that JSX is not created within a try block. Developers may incorrectly assume that wrapping JSX in try/catch will catch rendering errors, but React does not immediately render components when JSX is created - JSX is just a description of UI that will be rendered later. Error boundaries should be used instead.89See: https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary1011## Input Invariants12- Operates on HIRFunction (pre-reactive scope inference)13- Blocks are traversed in order14- Only runs when `outputMode === 'lint'`1516## Validation Rules17The pass errors when `JsxExpression` or `JsxFragment` instructions are found within a try block.1819**Error message:**20```21Error: Avoid constructing JSX within try/catch2223React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary.24```2526### Important distinction27- JSX in a **try block**: Error28- JSX in a **catch block** (not nested in outer try): Allowed29- JSX in a **catch block** (nested in outer try): Error3031## Algorithm321. Maintain a stack `activeTryBlocks` of currently active try statement handler block IDs332. For each block:34   - Remove the current block from `activeTryBlocks` if it matches a handler (we've exited the try scope)35   - If `activeTryBlocks` is not empty (we're inside a try block):36     - Check each instruction for `JsxExpression` or `JsxFragment`37     - If found, push an error38   - If the block's terminal is a `try` terminal, push its handler block ID to `activeTryBlocks`3940### Block tracking with `retainWhere`41The `retainWhere` utility is used to remove the current block from `activeTryBlocks` at the start of each block. When we reach a catch handler block, it gets removed from the active list, allowing JSX in catch blocks (unless there's an outer try).4243## Edge Cases4445### Allowed: JSX in catch (no outer try)46```javascript47// Valid - catch block is not inside a try48function Component() {49  try {50    doSomething();51  } catch {52    return <ErrorMessage />; // OK53  }54}55```5657### Error: JSX in catch with outer try58```javascript59// Error - catch is inside outer try60function Component() {61  try {62    try {63      doSomething();64    } catch {65      return <ErrorMessage />; // Error!66    }67  } catch {68    return null;69  }70}71```7273### Error: JSX assigned in try74```javascript75// Error - JSX creation is in try block76function Component() {77  let el;78  try {79    el = <div />; // Error here80  } catch {81    return null;82  }83  return el;84}85```8687### Finally blocks88The validation currently has TODOs for handling try/catch/finally properly. Files like `error.todo-invalid-jsx-in-try-with-finally.js` indicate these are known unsupported cases.8990## TODOs91Based on fixture naming patterns:92- `error.todo-invalid-jsx-in-try-with-finally.js` - Try blocks with finally clauses93- `error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js` - Nested try/catch in try with finally9495## Example9697### Fixture: `invalid-jsx-in-try-with-catch.js`9899**Input:**100```javascript101// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint"102function Component(props) {103  let el;104  try {105    el = <div />;106  } catch {107    return null;108  }109  return el;110}111```112113**Error:**114```115Error: Avoid constructing JSX within try/catch116117React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary.118119invalid-jsx-in-try-with-catch.ts:5:9120  3 |   let el;121  4 |   try {122> 5 |     el = <div />;123    |          ^^^^^^^ Avoid constructing JSX within try/catch124  6 |   } catch {125  7 |     return null;126  8 |   }127```128129**Why it fails:** The `<div />` JSX element is created inside a try block. If the developer expects this to catch errors from rendering the div, they will be surprised - the try/catch will only catch errors from creating the JSX object (which is rare), not from React actually rendering it later. The correct approach is to use an error boundary component to catch rendering errors.130131### Fixture: `invalid-jsx-in-catch-in-outer-try-with-catch.js`132133**Input:**134```javascript135// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint"136import {identity} from 'shared-runtime';137138function Component(props) {139  let el;140  try {141    let value;142    try {143      value = identity(props.foo);144    } catch {145      el = <div value={value} />;146    }147  } catch {148    return null;149  }150  return el;151}152```153154**Error:**155```156Error: Avoid constructing JSX within try/catch157158...159160invalid-jsx-in-catch-in-outer-try-with-catch.ts:11:11161   9 |       value = identity(props.foo);162  10 |     } catch {163> 11 |       el = <div value={value} />;164     |            ^^^^^^^^^^^^^^^^^^^^^ Avoid constructing JSX within try/catch165```166167**Why it fails:** Even though the JSX is in a catch block, that catch block is itself inside an outer try block. The outer try's catch won't catch rendering errors from the JSX any more than the inner try would.

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.