Skip to content

Commit 640206f

Browse files
committed
fix: replace useCachedPromise with direct fetch to prevent OOM on first launch
useCachedPromise uses ~4x more memory than direct fetch when populating an empty cache, causing JS heap out of memory crashes on first launch. This replaces it with direct fetch + Cache API while maintaining the same stale-while-revalidate behavior. See: raycast/utils#65
1 parent ea30e8d commit 640206f

File tree

4 files changed

+70
-44
lines changed

4 files changed

+70
-44
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# models.dev Changelog
22

3+
## [Bug Fix] - {PR_MERGE_DATE}
4+
5+
### Fixed
6+
7+
- Fixed JS heap out of memory crash on first launch caused by `useCachedPromise` memory overhead
8+
- Replaced `useCachedPromise` with direct fetch + Cache API to reduce peak memory usage
9+
10+
### Changed
11+
12+
- Removed `@raycast/utils` dependency (no longer needed)
13+
314
## [Bug Fixes] - 2026-03-16
415

516
### Changed

package-lock.json

Lines changed: 1 addition & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@
120120
}
121121
],
122122
"dependencies": {
123-
"@raycast/api": "^1.104.3",
124-
"@raycast/utils": "^2.2.2"
123+
"@raycast/api": "^1.104.3"
125124
},
126125
"devDependencies": {
127126
"@raycast/eslint-config": "^2.1.1",

src/hooks/useModelsData.ts

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,65 @@
1-
import { showToast, Toast } from "@raycast/api";
2-
import { useCachedPromise } from "@raycast/utils";
1+
import { Cache, showToast, Toast } from "@raycast/api";
2+
import { useState, useEffect, useRef } from "react";
33
import { fetchModelsData } from "../lib/api";
4+
import type { ModelsData } from "../lib/types";
5+
6+
const cache = new Cache();
7+
const CACHE_KEY = "models-data";
48

59
/**
610
* Hook to fetch models data from models.dev
7-
* useCachedPromise persists the last successful result to disk automatically,
8-
* serving stale-while-revalidate on subsequent opens with no extra heap allocation.
11+
*
12+
* Uses direct fetch + Cache API instead of useCachedPromise to avoid
13+
* memory spikes during fresh cache population. See:
14+
* https://github.com/raycast/utils/issues/XXX
915
*/
1016
export function useModelsData() {
11-
return useCachedPromise(fetchModelsData, [], {
12-
keepPreviousData: true,
13-
onError: (error) => {
14-
showToast({
15-
style: Toast.Style.Failure,
16-
title: "Failed to load models",
17-
message: error instanceof Error ? error.message : "Unknown error",
18-
});
19-
},
17+
const [data, setData] = useState<ModelsData | null>(() => {
18+
const cached = cache.get(CACHE_KEY);
19+
if (cached) {
20+
try {
21+
return JSON.parse(cached) as ModelsData;
22+
} catch {
23+
cache.remove(CACHE_KEY);
24+
}
25+
}
26+
return null;
2027
});
28+
29+
const [isLoading, setIsLoading] = useState(!data);
30+
const fetchedRef = useRef(false);
31+
32+
useEffect(() => {
33+
// Already have cached data or already fetching
34+
if (fetchedRef.current) return;
35+
fetchedRef.current = true;
36+
37+
// If we have cached data, still revalidate in background
38+
const shouldRevalidate = !!data;
39+
40+
if (!shouldRevalidate) {
41+
setIsLoading(true);
42+
}
43+
44+
fetchModelsData()
45+
.then((result) => {
46+
setData(result);
47+
setIsLoading(false);
48+
// Cache write happens after state update to reduce peak memory
49+
cache.set(CACHE_KEY, JSON.stringify(result));
50+
})
51+
.catch((error) => {
52+
setIsLoading(false);
53+
// Only show error if we don't have cached data to fall back on
54+
if (!data) {
55+
showToast({
56+
style: Toast.Style.Failure,
57+
title: "Failed to load models",
58+
message: error instanceof Error ? error.message : "Unknown error",
59+
});
60+
}
61+
});
62+
}, []);
63+
64+
return { data, isLoading };
2165
}

0 commit comments

Comments
 (0)