From c5e2342ca8da4ded0ddb042777ebb95cd4d9d5c0 Mon Sep 17 00:00:00 2001
From: Brian Vaughn
Date: Thu, 23 Feb 2023 23:30:41 -0500
Subject: [PATCH] Reversed getCacheKey and loadData params
---
.../src/examples/createCache/async.ts | 5 +--
.../src/examples/createCache/cache.ts | 9 ++----
.../src/examples/createCache/cacheWithKey.ts | 16 ++++++++++
.../src/examples/createCache/evict.ts | 4 +--
.../src/examples/createCache/hook.ts | 4 +--
.../src/examples/createCache/prefetch.ts | 4 +--
.../src/examples/createCache/suspense.ts | 5 +--
.../src/examples/createCache/sync.ts | 6 ++--
.../examples/createStreamingCache/cache.ts | 3 --
.../createStreamingCache/cacheWithKey.ts | 32 +++++++++++++++++++
.../suspense-website/src/examples/index.ts | 9 ++++++
.../src/routes/createCache.tsx | 17 +++++-----
.../src/routes/createStreamingCache.tsx | 19 +++++------
.../src/suspense/ImportCache.ts | 29 ++++++++---------
.../src/suspense/SyntaxParsingCache.ts | 2 +-
packages/suspense/CHANGELOG.md | 3 ++
.../suspense/src/cache/createCache.test.ts | 2 +-
packages/suspense/src/cache/createCache.ts | 6 +++-
.../src/cache/createStreamingCache.test.ts | 2 +-
.../src/cache/createStreamingCache.ts | 6 +++-
.../src/hooks/useCacheStatus.test.tsx | 2 +-
.../src/hooks/useStreamingValues.test.tsx | 9 ++----
22 files changed, 126 insertions(+), 68 deletions(-)
create mode 100644 packages/suspense-website/src/examples/createCache/cacheWithKey.ts
create mode 100644 packages/suspense-website/src/examples/createStreamingCache/cacheWithKey.ts
diff --git a/packages/suspense-website/src/examples/createCache/async.ts b/packages/suspense-website/src/examples/createCache/async.ts
index 54854ac..f59c4de 100644
--- a/packages/suspense-website/src/examples/createCache/async.ts
+++ b/packages/suspense-website/src/examples/createCache/async.ts
@@ -1,7 +1,8 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
// REMOVE_BEFORE
async function load(userId: string) {
- const userData = await exampleCache.fetchAsync(userId);
+ const userProfile = await userProfileCache.fetchAsync(userId);
+
// ...
}
diff --git a/packages/suspense-website/src/examples/createCache/cache.ts b/packages/suspense-website/src/examples/createCache/cache.ts
index 9719e4d..03efc41 100644
--- a/packages/suspense-website/src/examples/createCache/cache.ts
+++ b/packages/suspense-website/src/examples/createCache/cache.ts
@@ -1,12 +1,9 @@
import { createCache } from "suspense";
-export const exampleCache = createCache<[userId: string], JSON>(
- // Create unique key for params
- (userId: string) => userId,
-
- // Load data for params
+export const userProfileCache = createCache<[userId: string], JSON>(
async (userId: string) => {
const response = await fetch(`https://example.com/user?id=${userId}`);
- return await response.json();
+ const json = await response.json();
+ return json;
}
);
diff --git a/packages/suspense-website/src/examples/createCache/cacheWithKey.ts b/packages/suspense-website/src/examples/createCache/cacheWithKey.ts
new file mode 100644
index 0000000..7a26911
--- /dev/null
+++ b/packages/suspense-website/src/examples/createCache/cacheWithKey.ts
@@ -0,0 +1,16 @@
+import { createCache } from "suspense";
+
+class ApiClient {
+ async loadData(id: string) {
+ return JSON.parse("");
+ }
+}
+
+// REMOVE_BEFORE
+createCache<[client: ApiClient, id: string], JSON>(
+ // In this example, data is loaded by a "client" object
+ async (client: ApiClient, id: string) => client.loadData(id),
+
+ // The id parameter is sufficiently unique to be the key
+ (client: ApiClient, id: string) => id
+);
diff --git a/packages/suspense-website/src/examples/createCache/evict.ts b/packages/suspense-website/src/examples/createCache/evict.ts
index 484b892..6ea921b 100644
--- a/packages/suspense-website/src/examples/createCache/evict.ts
+++ b/packages/suspense-website/src/examples/createCache/evict.ts
@@ -1,6 +1,6 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
// REMOVE_BEFORE
const userId = "123";
-exampleCache.evict(userId);
+userProfileCache.evict(userId);
diff --git a/packages/suspense-website/src/examples/createCache/hook.ts b/packages/suspense-website/src/examples/createCache/hook.ts
index 4a1594a..a390d27 100644
--- a/packages/suspense-website/src/examples/createCache/hook.ts
+++ b/packages/suspense-website/src/examples/createCache/hook.ts
@@ -1,10 +1,10 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
// REMOVE_BEFORE
import { useCacheStatus } from "suspense";
function StatusBadge({ userId }: { userId: string }) {
- const status = useCacheStatus(exampleCache, userId);
+ const status = useCacheStatus(userProfileCache, userId);
switch (status) {
case "pending":
diff --git a/packages/suspense-website/src/examples/createCache/prefetch.ts b/packages/suspense-website/src/examples/createCache/prefetch.ts
index f2426f4..4dfbd64 100644
--- a/packages/suspense-website/src/examples/createCache/prefetch.ts
+++ b/packages/suspense-website/src/examples/createCache/prefetch.ts
@@ -1,4 +1,4 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
function getQueryParam(key: string): string {
return "dummy";
@@ -8,7 +8,7 @@ function getQueryParam(key: string): string {
const userId = getQueryParam("userId");
// Start loading user data eagerly, while route renders.
-exampleCache.prefetch(userId);
+userProfileCache.prefetch(userId);
function UserProfileRoute() {
// ...
diff --git a/packages/suspense-website/src/examples/createCache/suspense.ts b/packages/suspense-website/src/examples/createCache/suspense.ts
index 63de26e..8ddec03 100644
--- a/packages/suspense-website/src/examples/createCache/suspense.ts
+++ b/packages/suspense-website/src/examples/createCache/suspense.ts
@@ -1,7 +1,8 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
// REMOVE_BEFORE
function UserProfile({ userId }: { userId: string }) {
- const userData = exampleCache.fetchSuspense(userId);
+ const userProfile = userProfileCache.fetchSuspense(userId);
+
// ...
}
diff --git a/packages/suspense-website/src/examples/createCache/sync.ts b/packages/suspense-website/src/examples/createCache/sync.ts
index 9e40da7..1c3c5b4 100644
--- a/packages/suspense-website/src/examples/createCache/sync.ts
+++ b/packages/suspense-website/src/examples/createCache/sync.ts
@@ -1,9 +1,9 @@
-import { exampleCache } from "./cache";
+import { userProfileCache } from "./cache";
const userId = "fake";
// REMOVE_BEFORE
// Returns a cached value if one has already been saved in the cache
-const userDataOrUndefined = exampleCache.getValueIfCached(userId);
+const userProfileOrUndefined = userProfileCache.getValueIfCached(userId);
// Returns a cached value or throws if none has been loaded
-const userData = exampleCache.getValue(userId);
+const userProfile = userProfileCache.getValue(userId);
diff --git a/packages/suspense-website/src/examples/createStreamingCache/cache.ts b/packages/suspense-website/src/examples/createStreamingCache/cache.ts
index a746c43..d793380 100644
--- a/packages/suspense-website/src/examples/createStreamingCache/cache.ts
+++ b/packages/suspense-website/src/examples/createStreamingCache/cache.ts
@@ -6,9 +6,6 @@ export const exampleStreamingCache = createStreamingCache<
[path: string],
string
>(
- // Create unique key for params
- (path: string) => path,
-
// Stream data for params
async (notifier: StreamingProgressNotifier, path: string) => {
let loadedLines = 0;
diff --git a/packages/suspense-website/src/examples/createStreamingCache/cacheWithKey.ts b/packages/suspense-website/src/examples/createStreamingCache/cacheWithKey.ts
new file mode 100644
index 0000000..b8a8968
--- /dev/null
+++ b/packages/suspense-website/src/examples/createStreamingCache/cacheWithKey.ts
@@ -0,0 +1,32 @@
+import { createStreamingCache, StreamingProgressNotifier } from "suspense";
+
+class ApiClient {
+ async streamData(
+ id: string,
+ onData: (data: string[]) => void,
+ onComplete: () => void
+ ) {
+ return JSON.parse("");
+ }
+}
+
+// REMOVE_BEFORE
+export const exampleStreamingCache = createStreamingCache<
+ [client: ApiClient, id: string],
+ string
+>(
+ // In this example, data is streamed by a "client" object
+ async (
+ notifier: StreamingProgressNotifier,
+ client: ApiClient,
+ id: string
+ ) =>
+ client.streamData(
+ id,
+ (values: string[]) => notifier.update(values),
+ () => notifier.resolve()
+ ),
+
+ // The id parameter is sufficiently unique to be the key
+ (client: ApiClient, id: string) => id
+);
diff --git a/packages/suspense-website/src/examples/index.ts b/packages/suspense-website/src/examples/index.ts
index aec9b36..318b533 100644
--- a/packages/suspense-website/src/examples/index.ts
+++ b/packages/suspense-website/src/examples/index.ts
@@ -17,6 +17,9 @@ const createCache = {
cache: processExample(
readFileSync(join(__dirname, "createCache", "cache.ts"), "utf8")
),
+ cacheWithKey: processExample(
+ readFileSync(join(__dirname, "createCache", "cacheWithKey.ts"), "utf8")
+ ),
evict: processExample(
readFileSync(join(__dirname, "createCache", "evict.ts"), "utf8")
),
@@ -50,6 +53,12 @@ const createStreamingCache = {
cache: processExample(
readFileSync(join(__dirname, "createStreamingCache", "cache.ts"), "utf8")
),
+ cacheWithKey: processExample(
+ readFileSync(
+ join(__dirname, "createStreamingCache", "cacheWithKey.ts"),
+ "utf8"
+ )
+ ),
hook: processExample(
readFileSync(join(__dirname, "createStreamingCache", "hook.ts"), "utf8")
),
diff --git a/packages/suspense-website/src/routes/createCache.tsx b/packages/suspense-website/src/routes/createCache.tsx
index ec20b3f..41ea7c8 100644
--- a/packages/suspense-website/src/routes/createCache.tsx
+++ b/packages/suspense-website/src/routes/createCache.tsx
@@ -26,18 +26,17 @@ export default function CreateCacheRoute() {
.)
- Implementing one of these caches requires two methods:
-
- -
- One to compute a unique key from cache parameters, and
-
- - One to load the data
-
- For example, a cache that loads user data from a (JSON) API might look
- like this:
+ Implementing one of these caches typically only requires a single
+ method (to load the data). For example, a cache that loads user data
+ from a (JSON) API might look like this:
+
+ If one of the cache key parameters can't be stringified, a second
+ method should also be provided to compute a unique key.
+
+
diff --git a/packages/suspense-website/src/routes/createStreamingCache.tsx b/packages/suspense-website/src/routes/createStreamingCache.tsx
index f099af3..ac909a9 100644
--- a/packages/suspense-website/src/routes/createStreamingCache.tsx
+++ b/packages/suspense-website/src/routes/createStreamingCache.tsx
@@ -21,16 +21,17 @@ export default function CreateStreamingCacheRoute() {
it provides a subscription interface that can be used to re-render as
data incrementally loads.
- Implementing a streaming cache requires two methods:
-
- -
- One to compute a unique key from cache parameters, and
-
- -
- One to stream the data using an API like a
WebSocket
-
-
+
+ Like a regular cache, implementing a streaming cache typically only
+ requires a single method– to stream the data using an API like a{" "}
+ WebSocket
.
+
+
+ A second method can be provided to compute the cache key if one of the
+ parameters can't be stringified.
+
+
diff --git a/packages/suspense-website/src/suspense/ImportCache.ts b/packages/suspense-website/src/suspense/ImportCache.ts
index b04be92..db2dbfa 100644
--- a/packages/suspense-website/src/suspense/ImportCache.ts
+++ b/packages/suspense-website/src/suspense/ImportCache.ts
@@ -5,20 +5,17 @@ type Module = any;
export const {
fetchAsync: fetchModuleAsync,
fetchSuspense: fetchModuleSuspense,
-} = createCache<[string], Module>(
- (path: string) => path,
- async (path: string) => {
- switch (path) {
- case "@codemirror/lang-css":
- return await import("@codemirror/lang-css");
- case "@codemirror/lang-html":
- return await import("@codemirror/lang-html");
- case "@codemirror/lang-javascript":
- return await import("@codemirror/lang-javascript");
- case "@codemirror/lang-markdown":
- return await import("@codemirror/lang-markdown");
- default:
- throw Error(`Unknown path: ${path}`);
- }
+} = createCache<[string], Module>(async (path: string) => {
+ switch (path) {
+ case "@codemirror/lang-css":
+ return await import("@codemirror/lang-css");
+ case "@codemirror/lang-html":
+ return await import("@codemirror/lang-html");
+ case "@codemirror/lang-javascript":
+ return await import("@codemirror/lang-javascript");
+ case "@codemirror/lang-markdown":
+ return await import("@codemirror/lang-markdown");
+ default:
+ throw Error(`Unknown path: ${path}`);
}
-);
+});
diff --git a/packages/suspense-website/src/suspense/SyntaxParsingCache.ts b/packages/suspense-website/src/suspense/SyntaxParsingCache.ts
index 796cf49..6d1af62 100644
--- a/packages/suspense-website/src/suspense/SyntaxParsingCache.ts
+++ b/packages/suspense-website/src/suspense/SyntaxParsingCache.ts
@@ -35,7 +35,6 @@ export const {
fetchAsync: highlightSyntaxAsync,
fetchSuspense: highlightSyntaxSuspense,
} = createCache<[code: string, language: Language], ParsedTokens[]>(
- (code: string, language: Language) => `${code.length}-${language}`,
async (code: string, language: Language) => {
const languageExtension = await getLanguageExtension(language);
const parsedTokens: ParsedTokens[] = [];
@@ -159,6 +158,7 @@ export const {
return parsedTokens;
},
+ (code: string, language: Language) => `${code.length}-${language}`,
"SyntaxParsingCache"
);
diff --git a/packages/suspense/CHANGELOG.md b/packages/suspense/CHANGELOG.md
index b40334a..ac78222 100644
--- a/packages/suspense/CHANGELOG.md
+++ b/packages/suspense/CHANGELOG.md
@@ -1,5 +1,8 @@
# Changelog
+## 0.0.3
+* Swapped order of `getCacheKey` and `loadData` functions for both `createCache` and `createStreamingCache` so that `getCacheKey` could be optional.
+
## 0.0.2
The initial release includes the following APIs:
#### Cache types
diff --git a/packages/suspense/src/cache/createCache.test.ts b/packages/suspense/src/cache/createCache.test.ts
index d787b43..62fcf97 100644
--- a/packages/suspense/src/cache/createCache.test.ts
+++ b/packages/suspense/src/cache/createCache.test.ts
@@ -23,7 +23,7 @@ describe("createCache", () => {
getCacheKey = jest.fn();
getCacheKey.mockImplementation((key) => key.toString());
- cache = createCache<[string], string>(getCacheKey, fetch, "cache");
+ cache = createCache<[string], string>(fetch, getCacheKey, "cache");
});
describe("cache", () => {
diff --git a/packages/suspense/src/cache/createCache.ts b/packages/suspense/src/cache/createCache.ts
index b16037a..4a302cd 100644
--- a/packages/suspense/src/cache/createCache.ts
+++ b/packages/suspense/src/cache/createCache.ts
@@ -12,8 +12,8 @@ import { assertPendingRecord } from "../utils/assertPendingRecord";
import { isThennable } from "../utils/isThennable";
export function createCache, Value>(
- getKey: (...params: Params) => string,
load: (...params: Params) => Thennable | Value,
+ getKey: (...params: Params) => string = defaultGetKey,
debugLabel?: string
): Cache {
const recordMap = new Map>();
@@ -192,3 +192,7 @@ export function createCache, Value>(
subscribeToStatus,
};
}
+
+function defaultGetKey(...params: any[]): string {
+ return params.join(",");
+}
diff --git a/packages/suspense/src/cache/createStreamingCache.test.ts b/packages/suspense/src/cache/createStreamingCache.test.ts
index b851734..4963770 100644
--- a/packages/suspense/src/cache/createStreamingCache.test.ts
+++ b/packages/suspense/src/cache/createStreamingCache.test.ts
@@ -25,8 +25,8 @@ describe("createStreamingCache", () => {
);
cache = createStreamingCache<[string], string, any>(
- getCacheKey,
fetch,
+ getCacheKey,
"cache"
);
});
diff --git a/packages/suspense/src/cache/createStreamingCache.ts b/packages/suspense/src/cache/createStreamingCache.ts
index dc827ed..038149f 100644
--- a/packages/suspense/src/cache/createStreamingCache.ts
+++ b/packages/suspense/src/cache/createStreamingCache.ts
@@ -13,8 +13,8 @@ export function createStreamingCache<
Value,
AdditionalData = undefined
>(
- getKey: (...params: Params) => string,
load: (notifier: StreamingProgressNotifier, ...params: Params) => void,
+ getKey: (...params: Params) => string = defaultGetKey,
debugLabel?: string
): StreamingCache {
const streamingValuesMap = new Map<
@@ -140,3 +140,7 @@ export function createStreamingCache<
stream,
};
}
+
+function defaultGetKey(...params: any[]): string {
+ return params.join(",");
+}
diff --git a/packages/suspense/src/hooks/useCacheStatus.test.tsx b/packages/suspense/src/hooks/useCacheStatus.test.tsx
index e557051..f416d13 100644
--- a/packages/suspense/src/hooks/useCacheStatus.test.tsx
+++ b/packages/suspense/src/hooks/useCacheStatus.test.tsx
@@ -39,7 +39,7 @@ describe("useCacheStatus", () => {
getCacheKey = jest.fn();
getCacheKey.mockImplementation((key) => key.toString());
- cache = createCache<[string], string>(getCacheKey, fetch, "cache");
+ cache = createCache<[string], string>(fetch, getCacheKey, "cache");
lastRenderedStatus = undefined;
});
diff --git a/packages/suspense/src/hooks/useStreamingValues.test.tsx b/packages/suspense/src/hooks/useStreamingValues.test.tsx
index 3fa9c32..6043839 100644
--- a/packages/suspense/src/hooks/useStreamingValues.test.tsx
+++ b/packages/suspense/src/hooks/useStreamingValues.test.tsx
@@ -38,12 +38,9 @@ describe("useStreamingValue", () => {
notifiers = new Map();
- cache = createStreamingCache(
- (key) => key,
- (notifier, key) => {
- notifiers.set(key, notifier);
- }
- );
+ cache = createStreamingCache((notifier, key) => {
+ notifiers.set(key, notifier);
+ });
});
it("should re-render as values stream in", () => {