From e06f6939598ecba9d6bd2afdc4f73af6c6cc8c70 Mon Sep 17 00:00:00 2001 From: Quinn Ftw Date: Fri, 26 Dec 2025 02:16:20 -0800 Subject: [PATCH] fix(dashboard): stable sorting for service list to prevent position flashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Services now maintain consistent positions when health status updates arrive via WebSocket. Added useMemo with alphabetical sorting by name-instanceId to prevent visual "jumping" when the services array is updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../apps/dashboard/src/hooks/useServices.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/infrastructure/service-registry/apps/dashboard/src/hooks/useServices.ts b/infrastructure/service-registry/apps/dashboard/src/hooks/useServices.ts index 8dcfedd7d..310973505 100644 --- a/infrastructure/service-registry/apps/dashboard/src/hooks/useServices.ts +++ b/infrastructure/service-registry/apps/dashboard/src/hooks/useServices.ts @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useEffect, useCallback, useMemo } from 'react'; import axios from 'axios'; import { ServiceInfo } from '@service-registry/types'; @@ -9,11 +9,23 @@ interface UseServicesResult { refetch: () => void; } +// Stable sort key for consistent ordering +function getServiceSortKey(service: ServiceInfo): string { + return `${service.name}-${service.instanceId || 'default'}`; +} + export function useServices(): UseServicesResult { - const [services, setServices] = useState([]); + const [rawServices, setRawServices] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Sort services by name for stable positioning on updates + const services = useMemo(() => { + return [...rawServices].sort((a, b) => + getServiceSortKey(a).localeCompare(getServiceSortKey(b)) + ); + }, [rawServices]); + const fetchServices = useCallback(async () => { try { setLoading(true); @@ -24,8 +36,8 @@ export function useServices(): UseServicesResult { const response = await axios.get(`${apiUrl}/registry/services`); // Ensure response.data is an array - const services = Array.isArray(response.data) ? response.data : []; - setServices(services); + const fetchedServices = Array.isArray(response.data) ? response.data : []; + setRawServices(fetchedServices); } catch (err) { console.error('Failed to fetch services:', err); setError(err instanceof Error ? err.message : 'Failed to fetch services');