From a8427d09589faca6bdbcfae671569387e342045f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Engebr=C3=A5ten?= Date: Tue, 17 Feb 2026 15:01:28 +0100 Subject: [PATCH] fix(github-issues): revert problematic changes from performance optimization - Reverted createWithEqualityFn changes that caused infinite loop - Removed useRenderCount hook that caused rendering issues - Kept individual store subscriptions (more reliable than shallow) - Kept other optimizations: filtering early-return, investigation useMemos, debounce, task filtering --- .../src/renderer/components/GitHubIssues.tsx | 4 --- .../github-issues/hooks/useGitHubIssues.ts | 22 +++++------- .../src/renderer/hooks/useRenderCount.ts | 34 ------------------ .../src/renderer/stores/github/index.ts | 3 -- .../renderer/stores/github/issues-store.ts | 4 +-- apps/frontend/src/renderer/stores/utils.ts | 36 ------------------- 6 files changed, 10 insertions(+), 93 deletions(-) delete mode 100644 apps/frontend/src/renderer/hooks/useRenderCount.ts delete mode 100644 apps/frontend/src/renderer/stores/utils.ts diff --git a/apps/frontend/src/renderer/components/GitHubIssues.tsx b/apps/frontend/src/renderer/components/GitHubIssues.tsx index d6b88e07..489b1f70 100644 --- a/apps/frontend/src/renderer/components/GitHubIssues.tsx +++ b/apps/frontend/src/renderer/components/GitHubIssues.tsx @@ -44,7 +44,6 @@ import { } from "./ui/alert-dialog"; import { useMutationStore } from "../stores/github/mutation-store"; import { useToast } from "../hooks/use-toast"; -import { useRenderCount } from "../hooks/useRenderCount"; import type { GitHubIssue, InvestigationState, InvestigationDismissReason, SuggestedLabel } from "../../shared/types"; import type { GitHubIssuesProps } from "./github-issues/types"; @@ -69,9 +68,6 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP const { t } = useTranslation("common"); const { toast } = useToast(); - // Performance monitoring (dev only) - always call hook, it exits early in prod - useRenderCount('GitHubIssues', 5000); - const projects = useProjectStore((state) => state.projects); const selectedProjectId = useProjectStore((state) => state.selectedProjectId); const selectedProject = projects.find((p) => p.id === selectedProjectId); diff --git a/apps/frontend/src/renderer/components/github-issues/hooks/useGitHubIssues.ts b/apps/frontend/src/renderer/components/github-issues/hooks/useGitHubIssues.ts index 047722fd..34a7262b 100644 --- a/apps/frontend/src/renderer/components/github-issues/hooks/useGitHubIssues.ts +++ b/apps/frontend/src/renderer/components/github-issues/hooks/useGitHubIssues.ts @@ -5,24 +5,18 @@ import { loadGitHubIssues, loadMoreGitHubIssues, checkGitHubConnection, - shallow, } from "../../../stores/github"; import type { FilterState } from "../types"; export function useGitHubIssues(projectId: string | undefined) { - // Single subscription using shallow comparison - const { issues, isLoading, isLoadingMore, error, selectedIssueNumber, filterState, hasMore } = useIssuesStore( - (s) => ({ - issues: s.issues, - isLoading: s.isLoading, - isLoadingMore: s.isLoadingMore, - error: s.error, - selectedIssueNumber: s.selectedIssueNumber, - filterState: s.filterState, - hasMore: s.hasMore, - }), - shallow - ); + // Individual subscriptions to avoid re-render on unrelated state changes + const issues = useIssuesStore((s) => s.issues); + const isLoading = useIssuesStore((s) => s.isLoading); + const isLoadingMore = useIssuesStore((s) => s.isLoadingMore); + const error = useIssuesStore((s) => s.error); + const selectedIssueNumber = useIssuesStore((s) => s.selectedIssueNumber); + const filterState = useIssuesStore((s) => s.filterState); + const hasMore = useIssuesStore((s) => s.hasMore); const selectIssue = useIssuesStore((s) => s.selectIssue); const setFilterState = useIssuesStore((s) => s.setFilterState); diff --git a/apps/frontend/src/renderer/hooks/useRenderCount.ts b/apps/frontend/src/renderer/hooks/useRenderCount.ts deleted file mode 100644 index 227d7ea6..00000000 --- a/apps/frontend/src/renderer/hooks/useRenderCount.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useRef, useEffect } from 'react'; - -/** - * Hook to track component render count for performance monitoring (dev only). - * Usage: useRenderCount('GitHubIssues', 100); - */ -export function useRenderCount(componentName: string, logInterval = 100) { - const renderCount = useRef(0); - const lastLogTime = useRef(Date.now()); - - // Always increment (minimal overhead) - renderCount.current += 1; - - useEffect(() => { - // Only log in development - if (!import.meta.env.DEV) { - return; - } - - const now = Date.now(); - const elapsed = now - lastLogTime.current; - - if (elapsed >= logInterval) { - console.log( - `[Performance] ${componentName} rendered ${renderCount.current} times in ${elapsed}ms ` + - `(${Math.round(renderCount.current / (elapsed / 1000))} renders/sec)` - ); - renderCount.current = 0; - lastLogTime.current = now; - } - }); - - return renderCount.current; -} diff --git a/apps/frontend/src/renderer/stores/github/index.ts b/apps/frontend/src/renderer/stores/github/index.ts index ee8877e3..c065c7d9 100644 --- a/apps/frontend/src/renderer/stores/github/index.ts +++ b/apps/frontend/src/renderer/stores/github/index.ts @@ -51,9 +51,6 @@ export { checkGitHubConnection } from './sync-status-store'; -// Utilities -export { shallow } from '../utils'; - /** * Initialize all global GitHub listeners. * Call this once at app startup. diff --git a/apps/frontend/src/renderer/stores/github/issues-store.ts b/apps/frontend/src/renderer/stores/github/issues-store.ts index 00bb69aa..e140815f 100644 --- a/apps/frontend/src/renderer/stores/github/issues-store.ts +++ b/apps/frontend/src/renderer/stores/github/issues-store.ts @@ -1,4 +1,4 @@ -import { createWithEqualityFn } from 'zustand/traditional'; +import { create } from 'zustand'; import type { GitHubIssue } from '@shared/types'; export type IssueFilterState = 'open' | 'closed' | 'all'; @@ -39,7 +39,7 @@ interface IssuesState { getOpenIssuesCount: () => number; } -export const useIssuesStore = createWithEqualityFn((set, get) => ({ +export const useIssuesStore = create((set, get) => ({ // Initial state issues: [], isLoading: false, diff --git a/apps/frontend/src/renderer/stores/utils.ts b/apps/frontend/src/renderer/stores/utils.ts deleted file mode 100644 index 22b94307..00000000 --- a/apps/frontend/src/renderer/stores/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Shallow comparison function for Zustand selectors. - * Returns true if a and b are shallowly equal (same reference or same primitives). - */ -export function shallow(objA: T, objB: T): boolean { - if (Object.is(objA, objB)) { - return true; - } - - if ( - typeof objA !== 'object' || - objA === null || - typeof objB !== 'object' || - objB === null - ) { - return false; - } - - const keysA = Object.keys(objA as unknown as object); - const keysB = Object.keys(objB as unknown as object); - - if (keysA.length !== keysB.length) { - return false; - } - - for (const key of keysA) { - if ( - !Object.prototype.hasOwnProperty.call(objB, key) || - !Object.is((objA as any)[key], (objB as any)[key]) - ) { - return false; - } - } - - return true; -}