The Problem
Custom hooks are a powerful abstraction, but when they contain conditional logic that changes which or how many hooks are called, they violate React's Rules of Hooks and corrupt state.
Symptoms
- State from one useState call appears in another useState call
- useEffect runs with wrong dependencies
- Custom hook returns different shape on different renders
- App crashes only on specific user flows (not on initial load)
Real Error Scenario
```javascript // BAD: The number of hooks called depends on the data function useUserConfig(user) { if (!user) return null;
const [theme, setTheme] = useState('light'); const [notifications, setNotifications] = useState(true);
if (user.isAdmin) { const [auditLog, setAuditLog] = useState([]); // Only called for admins! return { theme, notifications, auditLog }; }
return { theme, notifications }; } ```
When a non-admin user logs in as admin (or vice versa), the hook calls a different number of useState calls. React's internal hook list gets corrupted.
How to Fix It
Fix 1: Always Call All Hooks
```javascript function useUserConfig(user) { const [theme, setTheme] = useState('light'); const [notifications, setNotifications] = useState(true); const [auditLog, setAuditLog] = useState([]); // Always called
if (!user) return null;
return { theme, notifications, auditLog: user.isAdmin ? auditLog : undefined }; } ```
Fix 2: Split Into Separate Hooks
```javascript function useBaseConfig() { const [theme] = useState('light'); const [notifications] = useState(true); return { theme, notifications }; }
function useAdminConfig() { const [auditLog] = useState([]); return { auditLog }; }
function UserPanel({ user }) { const config = useBaseConfig(); // Always called const adminConfig = user?.isAdmin ? useAdminConfig() : null; // Separate component would be better } ```
Fix 3: Extract Conditional Logic into a Separate Component
```javascript function AdminDashboard() { const { theme, notifications } = useBaseConfig(); const { auditLog } = useAdminConfig(); return <AdminView theme={theme} auditLog={auditLog} />; }
function UserDashboard() { const { theme, notifications } = useBaseConfig(); return <UserView theme={theme} />; }
function Dashboard({ user }) { if (!user) return <Login />; return user.isAdmin ? <AdminDashboard /> : <UserDashboard />; } ```
Each component has a stable hook call order, and the conditional is at the component level, not the hook level.