From 3ed9c7150e061cd9d35c3c98212a5bc03906a3cd Mon Sep 17 00:00:00 2001 From: Justas No Date: Fri, 17 Apr 2026 19:16:02 +0300 Subject: [PATCH] Fix: tag filter causes "Maximum update depth exceeded" crash Clicking a tag badge to filter subscriptions triggers an infinite re-render loop because: filteredSubscriptions is recomputed via .filter() on every render, producing a new array reference The useEffect that pushes this to the parent fires on every new reference Parent re-renders with new inline callback props, re-triggering the effect Fixed by: Wrapping filteredSubscriptions in useMemo Stabilizing parent callbacks with useCallback --- src/app/page.tsx | 17 ++++++++++++++--- src/components/SubscriptionList.tsx | 13 ++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 5948c26..007815c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import Header from '@/components/Header'; import SubscriptionList from '@/components/SubscriptionList'; import SubscriptionModal from '@/components/SubscriptionModal'; @@ -140,6 +140,17 @@ export default function Home() { ); }; + const handleFilteredSubscriptionsChange = useCallback( + (nextFilteredSubscriptions: Subscription[]) => { + setFilteredSubscriptions(nextFilteredSubscriptions); + }, + [] + ); + + const handleTagFilterChange = useCallback((tags: string[]) => { + setSelectedTags(tags); + }, []); + const handleDateClick = (date: Date) => { setSelectedDate(date); setSelectedSubscription(undefined); @@ -342,8 +353,8 @@ export default function Home() { onDelete={handleDeleteSubscription} onToggleInclude={handleToggleInclude} showCurrencySymbol={userConfig.showCurrencySymbol} - onFilteredSubscriptionsChange={(filteredSubs) => setFilteredSubscriptions(filteredSubs)} - onTagFilterChange={(tags) => setSelectedTags(tags)} + onFilteredSubscriptionsChange={handleFilteredSubscriptionsChange} + onTagFilterChange={handleTagFilterChange} /> 0 - ? subscriptions.filter(sub => - sub.tags && sub.tags.some(tag => tagFilters.includes(tag)) - ) - : subscriptions; + const filteredSubscriptions = useMemo(() => + tagFilters.length > 0 + ? subscriptions.filter(sub => + sub.tags && sub.tags.some(tag => tagFilters.includes(tag)) + ) + : subscriptions, + [subscriptions, tagFilters] + ); // Notify parent about filtered subscriptions useEffect(() => {