diff --git a/FIX_IMPLEMENT_NAVI.md b/FIX_IMPLEMENT_NAVI.md new file mode 100644 index 0000000..9fa4ab1 --- /dev/null +++ b/FIX_IMPLEMENT_NAVI.md @@ -0,0 +1,104 @@ +# To AI Agent: Fix Infinite Re-render Loop and Flickering "ĐANG TÌM ĐƯỜNG TỐI ƯU..." Label + +## 1. Context & Layout Bug Analysis +We are resolving a critical performance and UI bug inside the Map routing engine interface (`image.png`): +- **The Issue:** The floating loading indicator badge reading `"ĐANG TÌM ĐƯỜNG TỐI ƯU..."` keeps flashing, flickering, or appearing and disappearing in an infinite execution loop. +- **Root Cause:** This is caused by a broken React lifecycle loop. Every time the map triggers a directions route query, it toggles a loading state variable (`isSearching: true`). Once the route loads, the state updates (`isSearching: false`), forcing a component re-render. If the coordinates object (`origin`, `destination`) or the map instance ref inside the `useEffect` dependency array changes its reference pointer on every render, the hook fires *again*, creating an endless loop of API fetching and component flashing. + +--- + +## 2. Technical Execution Strategy +To terminate this flickering cycle, we must enforce a strict guard rail on the network/calculation trigger pipeline: +1. **Coordinate Reference Stability:** Deconstruct the input latitude/longitude objects into raw primitive string values (e.g., `origin.lat`, `origin.lng`) inside the hook dependency array to avoid object reference mutations triggering re-renders. +2. **Locking Ref Mechanism:** Implement a mutable React tracking reference (`const queryInProgress = useRef(false)`) to lock the execution window. If a fetch operation is active, block subsequent duplicate queries from firing. +3. **Clean Loading State Termination:** Turn off the search state flag explicitly only *after* the route geometry polyline has completely finished rendering onto the map viewport layout. + +--- + +## 3. Code Refactoring Blueprint + +Locate your map navigation layer or modal component (e.g., `LocationNavigationModal.tsx`) and refactor the execution lifecycle loop as defined below: + +```typescript +import React, { useEffect, useState, useRef } from 'react'; + +// Inside your Map Navigation Modal / Layer component wrapper: +export const LocationNavigationModal = ({ isOpen, routeData }) => { + const [isSearchingRoute, setIsSearchingRoute] = useState(false); + const mapInstanceRef = useRef(null); + + // CRITICAL FIX 2: Guard mechanism to block concurrent duplicated queries + const fetchLockRef = useRef(false); + + // Deconstruct coordinate primitives to secure a stable dependency array + const originLat = routeData?.origin?.lat; + const originLng = routeData?.origin?.lng; + const destLat = routeData?.destination?.lat; + const destLng = routeData?.destination?.lng; + + useEffect(() => { + if (!isOpen || !originLat || !originLng || !destLat || !destLng) return; + + // If an operation is already locked and active, bail out immediately to prevent loops + if (fetchLockRef.current) return; + + const calculateOptimalRoute = async () => { + try { + // 1. Activate loading feedback banner + setIsSearchingRoute(true); + fetchLockRef.current = true; // Engage execution lock + + console.log("Fetching route coordinates exactly once..."); + + // --- YOUR MAP COMPONENT ROUTING LOGIC START --- + // Example: const response = await directionsService.route({...}); + // await mapInstanceRef.current.drawPolyline(response); + // --- YOUR MAP COMPONENT ROUTING LOGIC END --- + + } catch (error) { + console.error("Failed to compile route optimization maps:", error); + } finally { + // 2. Safe, definitive termination of the tracking states + setIsSearchingRoute(false); + fetchLockRef.current = false; // Disengage execution lock + } + }; + + calculateOptimalRoute(); + + // Cleanup phase: Reset execution parameters when inputs dismantle or modal closes + return () => { + fetchLockRef.current = false; + setIsSearchingRoute(false); + }; + + /* CRITICAL FIX 1: Explicitly tracking primitives only. + Do NOT pass full 'routeData', 'mapInstanceRef' or object literals here! + */ + }, [isOpen, originLat, originLng, destLat, destLng]); + + return ( +
+ {/* Map Content Target Canvas */} + {/* Map Container */} -
+
= ({ isOpen, onClo )} - {loading && ( -
- Đang tải lộ trình... -
- )} + + {error && (
{error} @@ -170,4 +181,3 @@ export const LocationNavigationModal: React.FC = ({ isOpen, onClo
); }; - diff --git a/frontend/src/pages/TourDetailPage.tsx b/frontend/src/pages/TourDetailPage.tsx index 33a2bec..f4cf5e9 100644 --- a/frontend/src/pages/TourDetailPage.tsx +++ b/frontend/src/pages/TourDetailPage.tsx @@ -2585,13 +2585,7 @@ export const TourDetailPage = ({
)} - {/* Chỉ báo đang tìm đường */} - {isRoutingLoading && ( -
- - Đang tìm đường tối ưu... -
- )} +