# 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 */}