Additional ESLint rules specifically for React Hooks, enforcing Rules of Hooks and exhaustive dependencies for React 16.8+.
Provides React Hooks-specific ESLint rules that must be used alongside the main configuration.
/**
* React Hooks ESLint configuration
* Usage: extends: ['airbnb', 'airbnb/hooks']
*/
const hooksConfig = {
plugins: ['react-hooks'],
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
}
};Usage Examples:
// .eslintrc.js - Complete React setup with hooks
module.exports = {
extends: ['airbnb', 'airbnb/hooks']
};
// .eslintrc.js - With custom hook rules
module.exports = {
extends: ['airbnb', 'airbnb/hooks'],
rules: {
'react-hooks/exhaustive-deps': 'warn' // Downgrade to warning
}
};
// .eslintrc.js - TypeScript React with hooks
module.exports = {
extends: ['airbnb', 'airbnb/hooks'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
}
};Enforces React's Rules of Hooks to ensure hooks are called consistently.
/**
* Enforces Rules of Hooks
* - Only call hooks at the top level
* - Only call hooks from React functions
*/
const rulesOfHooks = {
'react-hooks/rules-of-hooks': 'error'
};What this rule catches:
// ❌ Bad - conditional hook call
function Component({ condition }) {
if (condition) {
const [state, setState] = useState(0); // Error!
}
return <div>...</div>;
}
// ❌ Bad - hook in loop
function Component({ items }) {
items.forEach(item => {
const [state, setState] = useState(item); // Error!
});
return <div>...</div>;
}
// ✅ Good - hooks at top level
function Component({ condition, items }) {
const [state, setState] = useState(0);
const [itemState, setItemState] = useState(items);
if (condition) {
// Use state here
}
return <div>...</div>;
}Ensures that useEffect, useMemo, and useCallback hooks declare all dependencies.
/**
* Enforces exhaustive dependency arrays
* - All dependencies must be listed in dependency array
* - Warns about missing dependencies
*/
const exhaustiveDeps = {
'react-hooks/exhaustive-deps': 'error'
};What this rule catches:
// ❌ Bad - missing dependency
function Component({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, []); // Error! Missing userId dependency
return <div>{user?.name}</div>;
}
// ❌ Bad - stale closure
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // Error! Using stale count
}, 1000);
return () => clearInterval(timer);
}, []); // Missing count dependency
return <div>{count}</div>;
}
// ✅ Good - complete dependencies
function Component({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]); // Correct dependency
return <div>{user?.name}</div>;
}
// ✅ Good - functional update
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prevCount => prevCount + 1); // Functional update
}, 1000);
return () => clearInterval(timer);
}, []); // No dependencies needed
return <div>{count}</div>;
}The hooks configuration requires the react-hooks plugin:
interface HooksPluginRequirement {
'eslint-plugin-react-hooks': string; // '^4.3.0'
}interface HooksConfiguration {
plugins: ['react-hooks'];
parserOptions: {
ecmaFeatures: {
jsx: boolean;
};
};
rules: {
'react-hooks/rules-of-hooks': 'error';
'react-hooks/exhaustive-deps': 'error';
};
}