Skip to content

Commit 10e5ad5

Browse files
authored
Conditionally deriving state is allowed (#8033)
1 parent aba6b86 commit 10e5ad5

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

src/content/reference/eslint-plugin-react-hooks/lints/set-state-in-render.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ version: rc
55

66
<Intro>
77

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

1010
</Intro>
1111

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

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

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

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

2626
### Invalid {/*invalid*/}
2727

2828
```js {expectedErrors: {'react-compiler': [4]}}
29-
// ❌ setState directly in render
29+
//Unconditional setState directly in render
3030
function Component({value}) {
3131
const [count, setCount] = useState(0);
3232
setCount(value); // Infinite loop!
@@ -59,6 +59,19 @@ function Component({user}) {
5959
const email = user?.email || '';
6060
return <div>{name}</div>;
6161
}
62+
63+
// ✅ Conditionally derive state from props and state from previous renders
64+
function Component({ items }) {
65+
const [isReverse, setIsReverse] = useState(false);
66+
const [selection, setSelection] = useState(null);
67+
68+
const [prevItems, setPrevItems] = useState(items);
69+
if (items !== prevItems) { // This condition makes it valid
70+
setPrevItems(items);
71+
setSelection(null);
72+
}
73+
// ...
74+
}
6275
```
6376
6477
## Troubleshooting {/*troubleshooting*/}
@@ -102,3 +115,5 @@ function Counter({max}) {
102115
```
103116
104117
Now the setter only runs in response to the click, React finishes the render normally, and `count` never crosses `max`.
118+
119+
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.

0 commit comments

Comments
 (0)