Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version: rc

<Intro>

Validates against setting state during render, which can trigger additional renders and potential infinite render loops.
Validates against unconditionally setting state during render, which can trigger additional renders and potential infinite render loops.

</Intro>

Expand All @@ -19,14 +19,14 @@ You can try it by upgrading the lint plugin [to the most recent RC version](/lea

## Rule Details {/*rule-details*/}

Calling `setState` during render triggers another render before the current one finishes. This creates an infinite loop that crashes your app.
Calling `setState` during render unconditionally triggers another render before the current one finishes. This creates an infinite loop that crashes your app.

## Common Violations {/*common-violations*/}

### Invalid {/*invalid*/}

```js {expectedErrors: {'react-compiler': [4]}}
// ❌ setState directly in render
//Unconditional setState directly in render
function Component({value}) {
const [count, setCount] = useState(0);
setCount(value); // Infinite loop!
Expand Down Expand Up @@ -59,6 +59,19 @@ function Component({user}) {
const email = user?.email || '';
return <div>{name}</div>;
}

// ✅ Conditionally derive state from props and state from previous renders
function Component({ items }) {
const [isReverse, setIsReverse] = useState(false);
const [selection, setSelection] = useState(null);

const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) { // This condition makes it valid
setPrevItems(items);
setSelection(null);
}
// ...
}
```
## Troubleshooting {/*troubleshooting*/}
Expand Down Expand Up @@ -102,3 +115,5 @@ function Counter({max}) {
```
Now the setter only runs in response to the click, React finishes the render normally, and `count` never crosses `max`.
In rare cases, you may need to adjust state based on information from previous renders. For those, follow [this pattern](https://react.dev/reference/react/useState#storing-information-from-previous-renders) of setting state conditionally.