tenantpilot/components/policy-explorer/PolicySearchContainer.tsx
Ahmed Darrazi 56088ca6c0
All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 1s
fix: Resolve search infinite loop issue
Fixed useEffect dependency problem in SearchInput that caused
infinite re-renders when searching with 2+ characters.

Changes:
- Removed onSearch from useEffect dependencies
- Added ESLint disable comment for exhaustive-deps
- Search now only triggers on debouncedQuery changes

Issue: Search spinner would hang indefinitely when typing 2 chars
Root cause: onSearch callback recreated on every render, causing loop
Solution: Only depend on debouncedQuery in useEffect
2025-12-07 22:59:07 +01:00

83 lines
2.3 KiB
TypeScript

'use client';
import { useState, useTransition } from 'react';
import { PolicyTable } from './PolicyTable';
import { SearchInput } from '@/components/search/SearchInput';
import { EmptyState } from '@/components/search/EmptyState';
import type { PolicySettingSearchResult } from '@/lib/actions/policySettings';
import { searchPolicySettings } from '@/lib/actions/policySettings';
import { toast } from 'sonner';
interface PolicySearchContainerProps {
initialPolicies: PolicySettingSearchResult[];
onPolicyClick: (policy: PolicySettingSearchResult) => void;
}
export function PolicySearchContainer({
initialPolicies,
onPolicyClick,
}: PolicySearchContainerProps) {
const [policies, setPolicies] = useState<PolicySettingSearchResult[]>(initialPolicies);
const [searchTerm, setSearchTerm] = useState('');
const [hasSearched, setHasSearched] = useState(false);
const [isPending, startTransition] = useTransition();
const handleSearch = (query: string) => {
setSearchTerm(query);
if (query.length === 0) {
// Reset to initial policies when search is cleared
setPolicies(initialPolicies);
setHasSearched(false);
return;
}
// Only search with 2 or more characters
if (query.length < 2) {
return;
}
startTransition(async () => {
try {
const result = await searchPolicySettings(query);
if (result.success) {
setPolicies(result.data ?? []);
setHasSearched(true);
} else {
toast.error(result.error ?? 'Search failed');
setPolicies([]);
setHasSearched(true);
}
} catch (error) {
toast.error('An unexpected error occurred');
setPolicies([]);
setHasSearched(true);
}
});
};
return (
<div className="space-y-4">
<SearchInput
onSearch={handleSearch}
isSearching={isPending}
/>
{policies.length === 0 && hasSearched && (
<EmptyState />
)}
{policies.length === 0 && !hasSearched && initialPolicies.length === 0 && (
<div className="text-center py-12 text-muted-foreground">
<p>Keine Policies gefunden - Starten Sie einen Sync</p>
</div>
)}
{policies.length > 0 && (
<PolicyTable policies={policies} onRowClick={onPolicyClick} />
)}
</div>
);
}