From 68b5e90a5114faf7924be1b5937f97df3fb97ee9 Mon Sep 17 00:00:00 2001 From: Daniel Saewitz Date: Tue, 24 Feb 2026 03:02:25 -0500 Subject: [PATCH] Add cleanup functions to several useEffects --- app/relisten/tabs/(relisten)/index.tsx | 11 +++- .../tabs/(relisten)/recently-played.tsx | 18 ++++--- app/useCacheAssets.ts | 11 +++- .../[month]/[day]/[trackSlug]/index.tsx | 54 ++++++++++++------- app/web/[artistSlug]/[year]/index.tsx | 36 ++++++++----- app/web/[artistSlug]/index.tsx | 20 +++++-- relisten/pages/legacy_migration.tsx | 8 ++- 7 files changed, 113 insertions(+), 45 deletions(-) diff --git a/app/relisten/tabs/(relisten)/index.tsx b/app/relisten/tabs/(relisten)/index.tsx index ef286639..de42f339 100644 --- a/app/relisten/tabs/(relisten)/index.tsx +++ b/app/relisten/tabs/(relisten)/index.tsx @@ -97,9 +97,18 @@ function StorageUsage() { const [showMigrationModal, setShowMigrationModal] = useState(false); useEffect(() => { + let cancelled = false; + (async () => { - setHasLegacyData(await legacyDatabaseExists()); + const exists = await legacyDatabaseExists(); + if (!cancelled) { + setHasLegacyData(exists); + } })(); + + return () => { + cancelled = true; + }; }, []); useFocusEffect( diff --git a/app/relisten/tabs/(relisten)/recently-played.tsx b/app/relisten/tabs/(relisten)/recently-played.tsx index fd22d9ca..238b5a47 100644 --- a/app/relisten/tabs/(relisten)/recently-played.tsx +++ b/app/relisten/tabs/(relisten)/recently-played.tsx @@ -217,19 +217,24 @@ export default function Page() { const artistsResults = useArtists(); useEffect(() => { + const controller = new AbortController(); + const getData = async () => { let params = ''; - // console.log(state.data[0]); if (state.data[0]) { params = `?lastSeenId=${state.data[0]}`; } - const data = await fetch(RelistenApiClient.API_BASE + `/v2/live/history${params}`).then( - (res) => res.json() - ); + try { + const data = await fetch(RelistenApiClient.API_BASE + `/v2/live/history${params}`, { + signal: controller.signal, + }).then((res) => res.json()); - // console.log(data); - call({ type: ACTIONS.UPDATE_DATA, data: data?.toReversed() }); + call({ type: ACTIONS.UPDATE_DATA, data: data?.toReversed() }); + } catch (e) { + if (e instanceof DOMException && e.name === 'AbortError') return; + throw e; + } }; getData(); @@ -239,6 +244,7 @@ export default function Page() { return () => { clearInterval(interval); + controller.abort(); }; }, []); diff --git a/app/useCacheAssets.ts b/app/useCacheAssets.ts index 7dc2d399..2b9b054a 100644 --- a/app/useCacheAssets.ts +++ b/app/useCacheAssets.ts @@ -18,6 +18,8 @@ export default function useCacheAssets() { // Load any resources or data that you need prior to rendering the app useEffect(() => { + let cancelled = false; + async function loadResourcesAndDataAsync() { try { const imageAssets = cacheImages([ToolbarRelisten]); @@ -28,14 +30,19 @@ export default function useCacheAssets() { } } } catch (e) { - // You might want to provide this error information to an error reporting service console.warn(e); } finally { - setIsAppReady(true); + if (!cancelled) { + setIsAppReady(true); + } } } loadResourcesAndDataAsync(); + + return () => { + cancelled = true; + }; }, []); return isAppReady; diff --git a/app/web/[artistSlug]/[year]/[month]/[day]/[trackSlug]/index.tsx b/app/web/[artistSlug]/[year]/[month]/[day]/[trackSlug]/index.tsx index d0578de7..d18d1af2 100644 --- a/app/web/[artistSlug]/[year]/[month]/[day]/[trackSlug]/index.tsx +++ b/app/web/[artistSlug]/[year]/[month]/[day]/[trackSlug]/index.tsx @@ -24,6 +24,9 @@ export default function Page() { return; } + let cancelled = false; + const timeoutIds: ReturnType[] = []; + (async () => { const show = await apiClient.showWithSourcesOnDate( String(artistSlug), @@ -35,14 +38,18 @@ export default function Page() { } ); + if (cancelled) return; + const showData = show.data; if (!showData) { logger.error(`Did not find a show matching ${year}-${month}-${day} for ${artistSlug}`); - setTimeout(() => { - router.push({ pathname: '/relisten/tabs' }); - }, 0); + timeoutIds.push( + setTimeout(() => { + if (!cancelled) router.push({ pathname: '/relisten/tabs' }); + }, 0) + ); return; } @@ -80,25 +87,36 @@ export default function Page() { const artistByUuid = groupByUuid([...artistsResults.data]); - setTimeout(() => { - const params: PushShowOptions = { - artist: artistByUuid[showData.artist_uuid], - showUuid: showData.uuid, - sourceUuid: sourceUuid, - overrideGroupSegment: '(artists)', - }; + timeoutIds.push( + setTimeout(() => { + if (cancelled) return; - if (autoplay && trackUuid) { - params.playTrackUuid = trackUuid; - } + const params: PushShowOptions = { + artist: artistByUuid[showData.artist_uuid], + showUuid: showData.uuid, + sourceUuid: sourceUuid, + overrideGroupSegment: '(artists)', + }; - router.push({ pathname: '/relisten/tabs' }); + if (autoplay && trackUuid) { + params.playTrackUuid = trackUuid; + } - setTimeout(() => { - pushShow(params); - }, 0); - }, 0); + router.push({ pathname: '/relisten/tabs' }); + + timeoutIds.push( + setTimeout(() => { + if (!cancelled) pushShow(params); + }, 0) + ); + }, 0) + ); })(); + + return () => { + cancelled = true; + timeoutIds.forEach(clearTimeout); + }; }, [artistSlug, year, month, day, artistsResults.data]); return ; diff --git a/app/web/[artistSlug]/[year]/index.tsx b/app/web/[artistSlug]/[year]/index.tsx index b583530f..bd70d037 100644 --- a/app/web/[artistSlug]/[year]/index.tsx +++ b/app/web/[artistSlug]/[year]/index.tsx @@ -15,6 +15,8 @@ export default function Page() { const years = useArtistYears(artist.data?.uuid || 'invalid'); useEffect(() => { + const timeoutIds: ReturnType[] = []; + if (years.data.artist !== null && years.data.years.length > 0) { const yearArtist = years.data.artist; const filteredYears = years.data.years.filter((y) => y.year === yearSlug); @@ -26,26 +28,36 @@ export default function Page() { const params = { artistUuid: yearArtist.uuid, yearUuid: year.uuid }; logger.info(`redirecting to ${newPath} ${JSON.stringify(params)}`); - setTimeout(() => { - router.push({ pathname: '/relisten/tabs' }); - + timeoutIds.push( setTimeout(() => { - router.push({ - pathname: newPath, - params, - }); - }, 0); - }, 0); + router.push({ pathname: '/relisten/tabs' }); + + timeoutIds.push( + setTimeout(() => { + router.push({ + pathname: newPath, + params, + }); + }, 0) + ); + }, 0) + ); } else { logger.error(`Did not find a year matching ${yearSlug}`); - setTimeout(() => { - router.push({ pathname: '/relisten/tabs' }); - }, 0); + timeoutIds.push( + setTimeout(() => { + router.push({ pathname: '/relisten/tabs' }); + }, 0) + ); } } else { logger.warn(`Cannot redirect artist=${years.data.artist}, years=${years.data.years.length}`); } + + return () => { + timeoutIds.forEach(clearTimeout); + }; }, [years.data, yearSlug]); return ; diff --git a/app/web/[artistSlug]/index.tsx b/app/web/[artistSlug]/index.tsx index 16c774ac..c89d5027 100644 --- a/app/web/[artistSlug]/index.tsx +++ b/app/web/[artistSlug]/index.tsx @@ -18,13 +18,23 @@ export default function Page() { const params = { artistUuid: artist.data?.uuid }; logger.info(`redirecting to ${newPath} ${JSON.stringify(params)}`); - setTimeout(() => { - router.push({ pathname: '/relisten/tabs' }); + const timeoutIds: ReturnType[] = []; + timeoutIds.push( setTimeout(() => { - router.push({ pathname: newPath, params }); - }, 0); - }, 0); + router.push({ pathname: '/relisten/tabs' }); + + timeoutIds.push( + setTimeout(() => { + router.push({ pathname: newPath, params }); + }, 0) + ); + }, 0) + ); + + return () => { + timeoutIds.forEach(clearTimeout); + }; } }, [artist.data]); diff --git a/relisten/pages/legacy_migration.tsx b/relisten/pages/legacy_migration.tsx index 37d59067..7619f55b 100644 --- a/relisten/pages/legacy_migration.tsx +++ b/relisten/pages/legacy_migration.tsx @@ -371,6 +371,8 @@ export function LegacyDataMigrationModal({ const realm = useRealm(); useEffect(() => { + let cancelled = false; + (async () => { const isIOS = Platform.OS === 'ios'; @@ -383,10 +385,14 @@ export function LegacyDataMigrationModal({ const legacyDbExists = await legacyDatabaseExists(); const eligibleForModal = legacyDbExists && hasNotDismissed && isIOS; - if (forceShow || (eligibleForModal && shouldMakeNetworkRequests)) { + if (!cancelled && (forceShow || (eligibleForModal && shouldMakeNetworkRequests))) { setModalVisible(true); } })(); + + return () => { + cancelled = true; + }; }, [forceShow]); const loadLegacyData = async () => {