7.8 KiB
AIS Test Map
This is a separate test map implementation created to test displaying data from the AIS server. It's located in a separate directory from the main base-map to allow independent testing and development.
Purpose
This test map was created to:
- Test AIS WebSocket server connectivity
- Display vessel data from the AIS stream
- Provide a simplified environment for debugging AIS data issues
- Serve as a reference implementation for AIS integration
Issues Fixed
React StrictMode Double Effect Issues
The primary issue was React's StrictMode in development mode causing double invocation of effects, leading to multiple simultaneous WebSocket connection attempts and immediate disconnections with errors like:
- "WebSocket is closed before the connection is established"
- Connection closed with error code 1006 (abnormal closure)
- "The network connection was lost"
JSON Parsing Errors
The original implementation had JSON parsing errors when the AIS server sent plain text messages like "Connected to AIS stream". The error was:
Error parsing WebSocket message: – SyntaxError: JSON Parse error: Unexpected identifier "Connected"
Solution: Implemented a React StrictMode-safe WebSocket connection with comprehensive state management and race condition prevention in src/ais-provider.tsx
:
Key Improvements:
- React StrictMode Protection: Prevents multiple simultaneous connection attempts using
isConnectingRef
flag - Component Mount Tracking: Uses
isMountedRef
to prevent operations on unmounted components - Connection State Guards: Comprehensive checks before attempting connections or state updates
- Exponential Backoff Reconnection: Automatic reconnection with increasing delays (1s, 2s, 4s, 8s, etc.)
- Connection Timeout Management: 10-second timeout with proper cleanup to prevent hanging connections
- Graceful Message Handling: Handles both JSON and plain text messages without errors
- Resource Cleanup: Proper cleanup of all timeouts, event handlers, and connections
- Race Condition Prevention: Multiple safeguards to prevent connection race conditions
// React StrictMode-safe connection logic
const connectSocket = useCallback(() => {
// Prevent multiple simultaneous connection attempts (React StrictMode protection)
if (isConnectingRef.current) {
console.log('Connection attempt already in progress, skipping...');
return;
}
// Check if component is still mounted
if (!isMountedRef.current) {
console.log('Component unmounted, skipping connection attempt');
return;
}
isConnectingRef.current = true;
const ws = new WebSocket('ws://localhost:3000/ws');
wsRef.current = ws;
// Connection timeout with proper cleanup
connectionTimeoutRef.current = setTimeout(() => {
if (ws.readyState === WebSocket.CONNECTING && isMountedRef.current) {
console.log('[TIMEOUT] Connection timeout, closing WebSocket');
isConnectingRef.current = false;
ws.close();
}
}, 10000);
ws.onopen = () => {
if (!isMountedRef.current) {
console.log('[OPEN] Component unmounted, closing connection');
ws.close();
return;
}
isConnectingRef.current = false;
// Handle successful connection...
};
// Robust message handling
ws.onmessage = (event) => {
try {
const messageData = event.data;
let data;
try {
data = JSON.parse(messageData);
} catch (parseError) {
console.log('Received plain text message:', messageData);
return;
}
// Handle JSON messages...
} catch (err) {
console.error('Error processing WebSocket message:', err);
}
};
}, []);
// Component cleanup with proper resource management
useEffect(() => {
isMountedRef.current = true;
// Small delay to prevent immediate double connection in StrictMode
const connectTimeout = setTimeout(() => {
if (isMountedRef.current) {
connectSocket();
}
}, 100);
return () => {
isMountedRef.current = false;
clearTimeout(connectTimeout);
// Clean up all resources...
};
}, [connectSocket]);
Project Structure
ais-test-map/
├── src/
│ ├── App.tsx # Main application component
│ ├── MapComponent.tsx # Map display component
│ ├── ais-provider.tsx # AIS WebSocket provider (fixed)
│ └── main.tsx # Application entry point
├── public/
├── package.json
├── vite.config.ts
├── test-websocket.cjs # WebSocket connection test
└── README.md # This file
Setup and Usage
Prerequisites
- Node.js installed
- AIS WebSocket server running on
ws://localhost:3000/ws
- Mapbox access token
Installation
cd ais-test-map
npm install
Running the Test Map
npm run dev
The map will be available at http://localhost:5173
Testing WebSocket Connection
To test the WebSocket connection independently:
node test-websocket.cjs
This will:
- Connect to the AIS WebSocket server
- Send test messages (bounding box, start stream)
- Display received messages (both JSON and plain text)
- Verify the connection works without parsing errors
Features
- Real-time AIS Data: Connects to WebSocket server for live vessel data
- Interactive Map: Mapbox-based map with vessel markers
- Bounding Box Updates: Automatically updates AIS data based on map viewport
- Error Handling: Robust error handling for connection issues
- Connection Status: Visual indicators for connection state
Configuration
The AIS provider connects to ws://localhost:3000/ws
by default. To change this, modify the WebSocket URL in src/ais-provider.tsx
:
const ws = new WebSocket('ws://your-server:port/ws');
Troubleshooting
Connection Issues
- Verify the AIS server is running:
lsof -i :3000
- Check WebSocket connection:
node test-websocket.cjs
- Review browser console for detailed error messages
No Vessel Data
- Ensure the map viewport covers an area with AIS data
- Check that the AIS stream has started (look for "Started AIS stream" in console)
- Verify bounding box is being sent correctly
Development Notes
This test map uses a simplified AIS provider compared to the main base-map implementation. Key differences:
- Simplified vessel data structure
- Direct WebSocket connection without complex reconnection logic
- Focused on testing and debugging rather than production use
Testing Results
React StrictMode Connection Test
🧪 Testing React StrictMode scenario (rapid double connections)...
Connection 1: ✅ Successful
Connection 2: ✅ Properly skipped (race condition prevented)
🔄 Testing sequential connections...
Sequential connection 1: ✅ Success
Sequential connection 2: ✅ Success
Sequential connection 3: ✅ Success
📈 Final Statistics:
Total connection attempts: 4
Successful connections: 4
Failed connections: 0
Success rate: 100.0%
🎉 All tests passed! React StrictMode fixes are working correctly.
Connection Stability Verification
✅ React StrictMode Protection: Double effects properly handled without race conditions
✅ WebSocket Connection: Stable connections without immediate disconnections
✅ Message Handling: Both JSON and plain text messages processed correctly
✅ Reconnection Logic: Exponential backoff working with proper cleanup
✅ Resource Management: All timeouts and connections properly cleaned up
✅ Bounding Box Updates: Map viewport changes trigger correct AIS data updates
✅ AIS Stream: Stream initialization and data flow working correctly