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
This commit is contained in:
Sondre Engebråten
2026-02-17 15:01:28 +01:00
parent 69b1861672
commit a8427d0958
6 changed files with 10 additions and 93 deletions
@@ -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);
@@ -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);
@@ -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;
}
@@ -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.
@@ -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<IssuesState>((set, get) => ({
export const useIssuesStore = create<IssuesState>((set, get) => ({
// Initial state
issues: [],
isLoading: false,
@@ -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<T>(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;
}