avoidChainingStateUpdatesAvoid chaining state changes. When possible, update "{{state}}" along with other relevant state simultaneously.small-rules/no-chain-state-updatesDisallow chaining state changes in an effect.
Ported from
eslint-plugin-react-you-might-not-need-an-effect/no-chain-state-updates.
The upstream rule points at the “Chains of
computations” section of the
React docs.
This flags an effect that depends on state and then writes different state. State A changes, the effect runs, it sets state B, you render again. That’s a chain, and chains are how one input change becomes four renders. If B happens to be in another effect’s dependencies, the chain keeps going.
The chain exists because someone wanted a computed value but reached for an effect instead of computing during render. Set both states where the change starts, or derive the follow-on value during render and skip that extra step. A fetch-then-set chain can be on purpose, so treat it as a nudge to look rather than a verdict.
avoidChainingStateUpdatesAvoid chaining state changes. When possible, update "{{state}}" along with other relevant state simultaneously.This rule accepts one options object after the severity.
import React, { useEffect, useState } from "@rbxts/react";
export function Game(): React.Element { const [round, setRound] = useState(1); const [isGameOver, setIsGameOver] = useState(false);
useEffect(() => { if (round > 10) { setIsGameOver(true); } }, [round]);
return ( <frame> <textlabel Text={isGameOver} /> <textbutton Text={round} onActivated={() => setRound(round + 1)} /> </frame> );}import React, { useState } from "@rbxts/react";
export function Game(): React.Element { const [round, setRound] = useState(1); const [isGameOver, setIsGameOver] = useState(false);
function handleRoundComplete(): void { const nextRound = round + 1; setRound(nextRound); if (nextRound > 10) { setIsGameOver(true); } }
return ( <frame> <textlabel Text={isGameOver} /> <textbutton Text={round} Event={{ Activated: handleRoundComplete }} /> </frame> );}