Plugin: react-featuresRules
no-direct-mutation-state
no-direct-mutation-state rule
Prevent direct mutation of this.state in React class components. This rule is part of eslint-plugin-react-features.
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Error (correctness) |
| Auto-Fix | ❌ No (requires setState) |
| Category | React |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | React class components |
Rule Details
Directly mutating this.state bypasses React's update mechanism, causing bugs and missing renders.
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🔄 No re-render | UI doesn't update | Use setState() |
| 🐛 State inconsistency | Batched updates break | Immutable updates |
| 🔍 Debug difficulty | State changes not tracked | Proper state flow |
Examples
❌ Incorrect
class Counter extends React.Component {
handleClick = () => {
this.state.count = this.state.count + 1; // Direct mutation!
this.state.items.push(newItem); // Mutating array
}
}✅ Correct
class Counter extends React.Component {
handleClick = () => {
this.setState({ count: this.state.count + 1 });
this.setState(prev => ({
items: [...prev.items, newItem] // Immutable update
}));
}
}
// Or use hooks (recommended)
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => setCount(c => c + 1);
}Configuration Examples
Basic Usage
{
rules: {
'react-features/no-direct-mutation-state': 'error'
}
}Related Rules
react-class-to-hooks- Migrate to hooks
Further Reading
- State and Lifecycle - React docs
- Using setState Correctly - setState reference
Known False Negatives
- Aliases:
const { items } = this.state; items.push(x)orconst s = this.state; s.count = 1mutate state through a local name and are not seen. - Mutation through a helper: passing
this.state.itemsto a function that mutates it is not followed. - Mutating calls are recognised by method name (
push,splice,sort, …) on a value read fromthis.state.
Not reported by design: any write in the constructor (where the initial state is set), and writes in a class that is not a React component.
Use this rule
npm install -D oxlint-plugin-react-features{ "jsPlugins": ["oxlint-plugin-react-features"], "rules": { "react-features/no-direct-mutation-state": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-react-features'], rules: { 'react-features/no-direct-mutation-state': 'error', }, },});oxlint-plugin-react-features re-exports eslint-plugin-react-features/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.