156 lines
5.3 KiB
Markdown
156 lines
5.3 KiB
Markdown
# Router Hook Error - ROOT CAUSE ANALYSIS (SOLVED)
|
|
|
|
## Date: December 10, 2025
|
|
|
|
## The Errors (FIXED)
|
|
|
|
- `/automation` → `useNavigate() may be used only in the context of a <Router> component.` ✅ FIXED
|
|
- `/planner/keywords` → `useLocation() may be used only in the context of a <Router> component.` ✅ FIXED
|
|
- `/planner/clusters` → `useLocation() may be used only in the context of a <Router> component.` ✅ FIXED
|
|
|
|
## ROOT CAUSE: Vite Bundling Multiple React Router Chunks
|
|
|
|
### The Real Problem
|
|
|
|
Components imported Router hooks from **TWO different packages**:
|
|
- Some used `import { useLocation } from 'react-router'`
|
|
- Some used `import { useLocation } from 'react-router-dom'`
|
|
- main.tsx used `import { BrowserRouter } from 'react-router-dom'`
|
|
|
|
Even though npm showed both packages as v7.9.5 and "deduped", **Vite bundled them into SEPARATE chunks**:
|
|
|
|
```
|
|
chunk-JWK5IZBO.js ← Contains 'react-router' code
|
|
chunk-U2AIREZK.js ← Contains 'react-router-dom' code
|
|
```
|
|
|
|
### Why This Caused the Error
|
|
|
|
1. **BrowserRouter** from `'react-router-dom'` (chunk-U2AIREZK.js) creates a Router context
|
|
2. **useLocation()** from `'react-router'` (chunk-JWK5IZBO.js) tries to read Router context
|
|
3. **Different chunks = Different module instances = Different React contexts**
|
|
4. The context from chunk-U2AIREZK is NOT accessible to hooks in chunk-JWK5IZBO
|
|
5. Hook can't find context → Error: "useLocation() may be used only in the context of a <Router> component"
|
|
|
|
### Evidence from Error Stack
|
|
|
|
```javascript
|
|
ErrorBoundary caught an error: Error: useLocation() may be used only in the context of a <Router> component.
|
|
at useLocation (chunk-JWK5IZBO.js?v=f560299f:5648:3) ← 'react-router' chunk
|
|
at TablePageTemplate (TablePageTemplate.tsx:182:20)
|
|
...
|
|
at BrowserRouter (chunk-U2AIREZK.js?v=f560299f:9755:3) ← 'react-router-dom' chunk
|
|
```
|
|
|
|
**Two different chunks = Context mismatch!**
|
|
|
|
### Component Stack Analysis
|
|
|
|
The error showed BrowserRouter WAS in the component tree:
|
|
|
|
```
|
|
TablePageTemplate (useLocation ERROR)
|
|
↓ Clusters
|
|
↓ ModuleGuard
|
|
↓ Routes
|
|
↓ App
|
|
↓ BrowserRouter ← Context provided HERE
|
|
```
|
|
|
|
Router context was available, but the hook was looking in the WRONG chunk's context.
|
|
|
|
## The Fix Applied
|
|
|
|
### 1. Changed ALL imports to use 'react-router-dom'
|
|
|
|
**Files updated (16 files):**
|
|
- `src/templates/TablePageTemplate.tsx`
|
|
- `src/templates/ContentViewTemplate.tsx`
|
|
- `src/components/navigation/ModuleNavigationTabs.tsx`
|
|
- `src/components/common/DebugSiteSelector.tsx`
|
|
- `src/components/common/SiteSelector.tsx`
|
|
- `src/components/common/SiteAndSectorSelector.tsx`
|
|
- `src/components/common/PageTransition.tsx`
|
|
- `src/pages/Linker/ContentList.tsx`
|
|
- `src/pages/Linker/Dashboard.tsx`
|
|
- `src/pages/Writer/ContentView.tsx`
|
|
- `src/pages/Writer/Content.tsx`
|
|
- `src/pages/Writer/Published.tsx`
|
|
- `src/pages/Writer/Review.tsx`
|
|
- `src/pages/Optimizer/ContentSelector.tsx`
|
|
- `src/pages/Optimizer/AnalysisPreview.tsx`
|
|
- `src/pages/Optimizer/Dashboard.tsx`
|
|
|
|
**Changed:**
|
|
```tsx
|
|
// BEFORE
|
|
import { useLocation } from 'react-router';
|
|
import { useNavigate } from 'react-router';
|
|
|
|
// AFTER
|
|
import { useLocation } from 'react-router-dom';
|
|
import { useNavigate } from 'react-router-dom';
|
|
```
|
|
|
|
### 2. Removed 'react-router' from package.json
|
|
|
|
**Before:**
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"react-router": "^7.1.5",
|
|
"react-router-dom": "^7.9.5"
|
|
}
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"react-router-dom": "^7.9.5"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Result
|
|
|
|
Now Vite bundles ALL Router code into a SINGLE chunk, ensuring Router context is shared across all components.
|
|
|
|
## Why Container Rebuild "Fixed" It Temporarily
|
|
|
|
When you rebuild containers, sometimes Vite's chunk splitting algorithm temporarily bundles both packages together, making the error disappear. But on the next HMR or rebuild, it splits them again → error returns.
|
|
|
|
## Testing Instructions
|
|
|
|
After these changes, test by visiting:
|
|
- https://app.igny8.com/planner/keywords → Should have NO errors
|
|
- https://app.igny8.com/planner/clusters → Should have NO errors
|
|
- https://app.igny8.com/automation → Should have NO errors
|
|
- https://app.igny8.com/account/plans → Should still have NO errors
|
|
|
|
Check browser console for zero Router-related errors.
|
|
|
|
## Key Learnings
|
|
|
|
1. **npm deduplication ≠ Vite bundling** - Even if npm shows packages as "deduped", Vite may still create separate chunks
|
|
2. **Module bundler matters** - The error wasn't in React or React Router, it was in how Vite split the code
|
|
3. **Import source determines chunk** - Importing from different packages creates different chunks with separate module instances
|
|
4. **React Context is per-module-instance** - Contexts don't cross chunk boundaries
|
|
5. **Consistency is critical** - ALL imports must use the SAME package to ensure single chunk
|
|
6. **Component stack traces reveal bundling** - Looking at chunk file names in errors shows the real problem
|
|
|
|
## Solution: Use ONE Package Consistently
|
|
|
|
For React Router v7 in Vite projects:
|
|
- ✅ Use `'react-router-dom'` exclusively
|
|
- ❌ Never mix `'react-router'` and `'react-router-dom'` imports
|
|
- ✅ Remove unused router packages from package.json
|
|
- ✅ Verify with: `grep -r "from 'react-router'" src/` (should return nothing)
|
|
|
|
---
|
|
|
|
## Status: ✅ RESOLVED
|
|
|
|
All imports standardized to `'react-router-dom'`. Error should no longer occur after HMR, container restarts, or cache clears.
|