React Hooks Error Fix
Problem Description
When navigating to process routes (/process/[id]), users encountered this error:
Unhandled Runtime Error
Error: Rendered more hooks than during the previous render.
Source
app/process/[id]/page.tsx (117:50) @ Studio
Root Cause
The error was caused by conditional returns happening before all React hooks were declared. This violated the Rules of Hooks:
❌ Before (Problematic Code):
const Studio = () => {
const params = useParams();
const processId = extractProcessId(params);
const [processValidation, setProcessValidation] = useState(/* ... */);
// ❌ PROBLEM: Early return before all hooks are declared
if (!processId) {
return <ErrorComponent />; // This breaks hook ordering!
}
// ❌ These hooks are declared AFTER conditional return
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
// ... more hooks
};
✅ After (Fixed Code):
const Studio = () => {
const params = useParams();
const processId = extractProcessId(params);
const [processValidation, setProcessValidation] = useState(/* ... */);
// ✅ ALL hooks declared first, in consistent order
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [selectedElement, setSelectedElement] = useState(null);
const { toasts, showError, showSuccess, removeToast } = useToast();
// All useCallback and useMemo hooks
const handleHoverNodeAdd = useCallback(/* ... */, [deps]);
const enhancedNodes = useMemo(/* ... */, [deps]);
// All useEffect hooks
useEffect(() => {
// Process validation logic
}, [processId]);
useEffect(() => {
// Keyboard shortcuts
}, [selectedElement, handleElementDelete, showSuccess, showError]);
// ✅ Conditional returns AFTER all hooks are initialized
if (!processId || !processValidation.isValid) {
if (processValidation.isValidating) {
return <LoadingComponent />;
}
return <ErrorComponent />;
}
return <MainComponent />;
};
Key Changes Made
1. Moved All Hook Declarations to Top
useState,useRef,useCallback,useMemo,useEffectall declared first- Consistent hook order across all renders
2. Process Validation in useEffect
- Moved validation logic to
useEffectinstead of inline - Handles both missing process ID and invalid process scenarios
3. Conditional Returns After Hooks
- All conditional returns moved to bottom of component
- Ensures hooks are called in same order every render
4. State-Driven Error Handling
const [processValidation, setProcessValidation] = useState<{
isValidating: boolean;
isValid: boolean;
error?: string
}>({ isValidating: true, isValid: false });
// useEffect updates this state based on validation results
// Conditional rendering based on state, not direct checks
Rules of Hooks Compliance
The fix ensures compliance with React's Rules of Hooks:
-
✅ Only Call Hooks at the Top Level
- No hooks inside loops, conditions, or nested functions
- All hooks declared before any conditional logic
-
✅ Only Call Hooks from React Functions
- All hooks called from functional component
- Consistent call order across renders
-
✅ Hooks Called in Same Order Every Time
- No conditional hook calls
- Same number of hooks on every render
Testing
Before Fix:
- ❌ Page crashed with hooks error
- ❌ Component re-renders broke hook ordering
- ❌ Navigation between processes failed
After Fix:
- ✅ Page loads successfully (
HTTP 200) - ✅ Process validation works correctly
- ✅ Error states display properly
- ✅ No hook ordering violations
Test Commands:
# Valid process ID
curl -s http://localhost:13008/process/0caab44d-b212-4d0b-9dc1-9215ab184f4b
# Result: ✅ Loads successfully
# Invalid process ID
curl -s http://localhost:13008/process/invalid-process-id
# Result: ✅ Shows error message (client-side)
Prevention Guidelines
To prevent similar issues in the future:
- Always declare ALL hooks first in component
- Never use conditional returns before all hooks are declared
- Use state-driven conditional rendering instead of early returns
- Group hooks by type: useState, useRef, useCallback, useMemo, useEffect
- Test hook ordering by navigating between different states
Files Modified
apps/studio/app/process/[id]/page.tsx- Fixed hook orderingapps/studio/lib/utils/process-validation.ts- Added validation utilitiesdocs/studio/hooks-fix-documentation.md- This documentation
Benefits
- ✅ Eliminates React hooks errors
- ✅ Improves component stability
- ✅ Better error handling UX
- ✅ Consistent rendering behavior
- ✅ Future-proof hook usage patterns