equinor / equinor/fusion-framework

Fix React Router v7 compatibility in legacy-interopt package

Open
#3,388 0 comments 0 reactions 0 assignees View on GitHub
πŸ‘Ύ React 🚧 chore 🧬 Modules
Dominant language
TypeScript
Stars
10
Forks
10
Avg merge
19h 40m
Merged PRs (30d)
150

Description

# React Router v5 to v7 Migration Guide

> ref: [chore: bump react-router-dom from 5.3.4 to 7.9.1 #3380](https://github.com/equinor/fusion-framework/pull/3380)

## TL;DR - Quick Fix

**Problem**: Build failing due to `Router` component API change in React Router v7
**Solution**: Replace `Router` with `BrowserRouter` in legacy-interopt package
**Time**: ~30 minutes + testing

```bash
# 1. Fix the breaking change
# File: packages/react/legacy-interopt/src/create-legacy-render.tsx:71
-
+

# 2. Update types
# File: packages/react/legacy-interopt/package.json
- "@types/react-router-dom": "^5.3.3"
+ "@types/react-router-dom": "^7.9.1"

# 3. Test
pnpm build
```

## Current Build Error

```typescript
// packages/react/legacy-interopt/src/create-legacy-render.tsx(71,28)
error TS2322: Type '{ children: ReactNode; key: string; history: History; }'
is not assignable to type 'IntrinsicAttributes & RouterProps'.
Property 'history' does not exist on type 'IntrinsicAttributes & RouterProps'.
```

## Root Cause Analysis

### What Broke
React Router v6+ removed the `history` prop from the `Router` component:

```tsx
// ❌ BREAKING (React Router v5)

{children}

// βœ… FIXED (React Router v6+)

{children}

```

### Why This Happened
- **React Router v5**: `Router` component accepted custom `history` instances
- **React Router v6+**: `Router` component no longer accepts `history` prop
- **Our Code**: Legacy-interopt still uses the old v5 pattern

### What's NOT Broken
- βœ… **Nested Histories**: Core architecture preserved (uses `v5Compat: true`)
- βœ… **Navigation Module**: Uses `@remix-run/router` (fully compatible)
- βœ… **Modern Apps**: Already use `RouterProvider` pattern
- βœ… **Type Safety**: Just need to update `@types/react-router-dom`

## Technical Details

### Architecture Overview
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Framework Layer (@remix-run/router) β”‚ ← Stable, no changes needed
β”‚ - Route creation β”‚
β”‚ - History management β”‚
β”‚ - Navigation logic β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Application Layer (react-router) β”‚ ← Updated to v7
β”‚ - UI components β”‚
β”‚ - React hooks β”‚
β”‚ - User interactions β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Package Status
| Package | Pattern | v7 Compatible | Action Needed |
|---------|---------|---------------|---------------|
| `dev-portal` | `RouterProvider` | βœ… Yes | None |
| `cookbooks/*` | `RouterProvider` | βœ… Yes | None |
| `legacy-interopt` | `Router` | ❌ No | **Fix Router component** |

### Why Nested Histories Still Work
The nested history logic operates at the **Navigator level**, not the React Router level:

```typescript
// This logic is UNCHANGED and still works
useLayoutEffect(() => {
return framework.modules.navigation.navigator.listen((update) => {
// Custom nested history synchronization
const appLocation = createLocation(/* ... */);
history.push(appLocation); // Uses Navigator, not React Router
});
}, [appKey, basename, framework, history]);
```

## Step-by-Step Fix

### Step 1: Fix Router Component
**File**: `packages/react/legacy-interopt/src/create-legacy-render.tsx`

```tsx
// Line 71: Replace this

{children}

// With this

{children}

```

**Why this works**: The nested history logic is handled by the `Navigator` class, not the React Router component.

### Step 2: Update Type Definitions
**File**: `packages/react/legacy-interopt/package.json`

```json
{
"devDependencies": {
"@types/react-router-dom": "^7.9.1"
}
}
```

### Step 3: Verify v5Compat Mode
**File**: `packages/modules/navigation/src/createHistory.ts`

Ensure all history types use `v5Compat: true`:
```typescript
return createBrowserHistory({ v5Compat: true });
return createHashHistory({ v5Compat: true });
return createMemoryHistory({ v5Compat: true });
```

### Step 4: Test the Fix
```bash
# Build all packages
pnpm build

# Test specific package
pnpm --filter @equinor/fusion-framework-legacy-interopt build

# Run tests
pnpm test
```

## Testing Checklist

### Build Verification
- [ ] `pnpm build` passes without errors
- [ ] TypeScript compilation succeeds
- [ ] No new linting errors

### Legacy App Integration
- [ ] Legacy app loads without errors
- [ ] Navigation between routes works
- [ ] Context switching functions correctly
- [ ] Nested history synchronization works

### Modern App Testing
- [ ] RouterProvider usage works
- [ ] Navigation hooks function correctly
- [ ] Route rendering is correct

## Navigation Module Analysis

### Current Architecture (No Changes Needed)
- **Framework**: Uses `@remix-run/router@1.23.0` (stable, mature)
- **Applications**: Use `react-router-dom@7.9.1` (latest features)
- **Compatibility**: βœ… **FULLY COMPATIBLE**

### Why Keep Current Architecture
1. **Framework Agnostic**: Navigation module independent of React Router versions
2. **Stable Core**: `@remix-run/router` is the core routing engine
3. **Version Independence**: No tight coupling between framework and apps
4. **Future-Proof**: Can adapt to future React Router versions

### Route Compatibility
```typescript
// Navigation Module creates router
const router = createRouter({
basename: '/apps/my-app',
history: customHistory,
routes: AgnosticRouteObject[], // ← Compatible with React Router v7
future: {
v7_prependBasename: true, // ← Already v7-ready!
},
});

// Applications consume seamlessly
// ← Works perfectly
```

## Deprecation Notice

> **⚠️ IMPORTANT**: `@equinor/fusion-framework-legacy-interopt` is scheduled for deprecation in **October 2024**. This React Router v7 fix is a **temporary solution** while planning the transition to modern React Router patterns for legacy applications.

### Long-term Planning (Post-October 2024)
1. **Plan Legacy App Migration**: Prepare for deprecation of legacy-interopt package
2. **Modern Router Patterns**: Migrate legacy apps to use modern React Router patterns
3. **Framework Integration**: Ensure legacy apps can integrate with modern navigation module
4. **Timeline**: Complete migration before October 2024 deprecation

## Risk Assessment

### Low Risk Factors
- βœ… Nested histories architecture is preserved
- βœ… v5Compat flag maintains v5 behavior
- βœ… Most packages already use modern patterns
- βœ… Framework abstraction provides good isolation

### Mitigation Strategies
1. **Incremental Testing**: Test each package individually
2. **Rollback Plan**: Keep v5 branch for quick rollback
3. **Legacy App Testing**: Comprehensive testing of legacy integration
4. **Type Safety**: Update all type definitions

## Common Issues & Solutions

### Issue: TypeScript errors after fix
**Solution**: Ensure `@types/react-router-dom` is updated to v7.9.1

### Issue: Legacy app navigation not working
**Solution**: Verify `v5Compat: true` is set in history creation

### Issue: Build still failing
**Solution**: Check that all `Router` components are replaced with `BrowserRouter`

## Summary

### What Needs to Change
- **1 file**: `packages/react/legacy-interopt/src/create-legacy-render.tsx` (Router β†’ BrowserRouter)
- **1 package**: `@types/react-router-dom` (v5.3.3 β†’ v7.9.1)

### What Stays the Same
- **Navigation Module**: No changes needed (uses `@remix-run/router`)
- **Nested Histories**: Core architecture preserved
- **Modern Apps**: Already compatible with v7

### Estimated Effort
- **Critical Fix**: 30 minutes
- **Testing**: 2-4 hours
- **Total**: Half day

### Success Criteria
- [ ] Build passes
- [ ] Legacy apps integrate correctly
- [ ] Nested histories work as expected
- [ ] Modern apps function normally
- [ ] Navigation module remains agnostic

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.