fix: Zustand selector anti-pattern causing infinite re-render + plugin loader path

- Fix usePluginStore(s => s.getAll*()) selectors that returned new arrays
  on every call, causing infinite re-render loops and permanent spinner
- Affected: Sidebar, Settings, ContactDetail, ContactEditModal, PluginRouteRenderer
- Use usePluginStore(s => s.manifests) + useMemo instead
- Fix PluginRouteRenderer: show spinner instead of null when manifests loading
- Fix PluginLoader: @/ path replacement ../ -> ../../ for correct dynamic imports
This commit is contained in:
Agent Zero
2026-07-24 19:29:48 +02:00
parent 6e930b814e
commit 4b9cb5a098
6 changed files with 57 additions and 16 deletions
+8 -2
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import clsx from 'clsx';
import { NavLink, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
@@ -38,7 +38,13 @@ export function Sidebar() {
const { t } = useTranslation();
const { sidebarOpen, setSidebarOpen } = useUIStore();
const location = useLocation();
const pluginMenuItems = usePluginStore(s => s.getAllMenuItems());
const manifests = usePluginStore(s => s.manifests);
const pluginMenuItems = useMemo(
() => manifests
.flatMap((m) => m.menu_items)
.sort((a, b) => a.order - b.order),
[manifests]
);
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());