Phase 0: Fix infinite loop in AppSidebar and module settings loading

- Fixed infinite loop by memoizing moduleEnabled with useCallback
- Fixed useEffect dependencies to prevent re-render loops
- Added loading check to prevent duplicate API calls
- Fixed setState calls to only update when values actually change
- Removed unused import (isModuleEnabled from modules.config)
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-16 19:13:12 +00:00
parent ab6b6cc4be
commit f195b6a72a
2 changed files with 33 additions and 21 deletions

Binary file not shown.

View File

@@ -21,7 +21,6 @@ import SidebarWidget from "./SidebarWidget";
import { APP_VERSION } from "../config/version";
import { useAuthStore } from "../store/authStore";
import { useSettingsStore } from "../store/settingsStore";
import { isModuleEnabled } from "../config/modules.config";
import ApiStatusIndicator from "../components/sidebar/ApiStatusIndicator";
type NavItem = {
@@ -40,7 +39,7 @@ const AppSidebar: React.FC = () => {
const { isExpanded, isMobileOpen, isHovered, setIsHovered } = useSidebar();
const location = useLocation();
const { user } = useAuthStore();
const { moduleEnableSettings, isModuleEnabled: checkModuleEnabled } = useSettingsStore();
const { moduleEnableSettings, isModuleEnabled: checkModuleEnabled, loadModuleEnableSettings, loading: settingsLoading } = useSettingsStore();
// Show admin menu only for users in aws-admin account
const isAwsAdminAccount = Boolean(
@@ -48,11 +47,11 @@ const AppSidebar: React.FC = () => {
user?.role === 'developer' // Also show for developers as fallback
);
// Helper to check if module is enabled
const moduleEnabled = (moduleName: string): boolean => {
// Helper to check if module is enabled - memoized to prevent infinite loops
const moduleEnabled = useCallback((moduleName: string): boolean => {
if (!moduleEnableSettings) return true; // Default to enabled if not loaded
return checkModuleEnabled(moduleName);
};
}, [moduleEnableSettings, checkModuleEnabled]);
const [openSubmenu, setOpenSubmenu] = useState<{
sectionIndex: number;
@@ -68,6 +67,15 @@ const AppSidebar: React.FC = () => {
[location.pathname]
);
// Load module enable settings on mount (only once)
useEffect(() => {
if (!moduleEnableSettings && !settingsLoading) {
loadModuleEnableSettings().catch((error) => {
console.warn('Failed to load module enable settings:', error);
});
}
}, []); // Empty dependency array - only run on mount
// Define menu sections with useMemo to prevent recreation on every render
// Filter out disabled modules based on module enable settings
const menuSections: MenuSection[] = useMemo(() => {
@@ -196,7 +204,7 @@ const AppSidebar: React.FC = () => {
],
},
];
}, [moduleEnableSettings, moduleEnabled]);
}, [moduleEnabled]);
// Admin section - only shown for users in aws-admin account
const adminSection: MenuSection = useMemo(() => ({
@@ -282,14 +290,6 @@ const AppSidebar: React.FC = () => {
: menuSections;
}, [isAwsAdminAccount, menuSections, adminSection]);
// Load module enable settings on mount
useEffect(() => {
const { loadModuleEnableSettings } = useSettingsStore.getState();
if (!moduleEnableSettings) {
loadModuleEnableSettings();
}
}, [moduleEnableSettings]);
useEffect(() => {
const currentPath = location.pathname;
let foundMatch = false;
@@ -305,9 +305,15 @@ const AppSidebar: React.FC = () => {
});
if (shouldOpen) {
setOpenSubmenu({
sectionIndex,
itemIndex,
setOpenSubmenu((prev) => {
// Only update if different to prevent infinite loops
if (prev?.sectionIndex === sectionIndex && prev?.itemIndex === itemIndex) {
return prev;
}
return {
sectionIndex,
itemIndex,
};
});
foundMatch = true;
}
@@ -330,10 +336,16 @@ const AppSidebar: React.FC = () => {
// scrollHeight should work even when height is 0px due to overflow-hidden
const scrollHeight = element.scrollHeight;
if (scrollHeight > 0) {
setSubMenuHeight((prevHeights) => ({
...prevHeights,
[key]: scrollHeight,
}));
setSubMenuHeight((prevHeights) => {
// Only update if height changed to prevent infinite loops
if (prevHeights[key] === scrollHeight) {
return prevHeights;
}
return {
...prevHeights,
[key]: scrollHeight,
};
});
}
}
}, 50);