void
onSmokeAPISettings?: (gameId: string) => void
onRate?: (gameId: string) => void
+ onRefresh: () => void
reportingEnabled?: boolean
}
@@ -18,7 +20,7 @@ interface GameListProps {
* Main game list component
* Displays games in a grid with search and filtering applied
*/
-const GameList = ({ games, isLoading, onAction, onEdit, onSmokeAPISettings, onRate, reportingEnabled }: GameListProps) => {
+const GameList = ({ games, isLoading, onAction, onEdit, onSmokeAPISettings, onRate, onRefresh, reportingEnabled }: GameListProps) => {
const [imagesPreloaded, setImagesPreloaded] = useState(false)
// Sort games alphabetically by title
@@ -45,7 +47,18 @@ const GameList = ({ games, isLoading, onAction, onEdit, onSmokeAPISettings, onRa
return (
-
Games ({games.length})
+
+
Games ({games.length})
+ }
+ iconOnly
+ />
+
{!imagesPreloaded && games.length > 0 && (
(null)
+ const hasLoadedRef = useRef(false)
// LoadGames function outside of the useEffect to make it reusable
const loadGames = useCallback(async () => {
@@ -91,9 +92,13 @@ export function useGames() {
}
}
- // Initialize event listeners and then load games
+ // Initialize event listeners and then load games. Guarded by a ref
+ // (not isInitialLoad state) so this effect only ever runs once putting
+ // isInitialLoad in the dep array would re-subscribe the listeners the
+ // moment it flips to false, needlessly tearing down and recreating them.
setupEventListeners().then(() => {
- if (isInitialLoad) {
+ if (!hasLoadedRef.current) {
+ hasLoadedRef.current = true
loadGames().catch(console.error)
}
})
@@ -102,7 +107,7 @@ export function useGames() {
return () => {
unlisteners.forEach((fn) => fn())
}
- }, [loadGames, isInitialLoad])
+ }, [loadGames])
// Helper function to update a specific game in state
const updateGame = useCallback((updatedGame: Game) => {