Skip to content

Commit

Permalink
Reversed getCacheKey and loadData params
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Feb 24, 2023
1 parent cda59bf commit c5e2342
Show file tree
Hide file tree
Showing 22 changed files with 126 additions and 68 deletions.
5 changes: 3 additions & 2 deletions packages/suspense-website/src/examples/createCache/async.ts
Original file line number Diff line number Diff line change
@@ -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);

// ...
}
9 changes: 3 additions & 6 deletions packages/suspense-website/src/examples/createCache/cache.ts
Original file line number Diff line number Diff line change
@@ -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;
}
);
16 changes: 16 additions & 0 deletions packages/suspense-website/src/examples/createCache/cacheWithKey.ts
Original file line number Diff line number Diff line change
@@ -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
);
4 changes: 2 additions & 2 deletions packages/suspense-website/src/examples/createCache/evict.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { exampleCache } from "./cache";
import { userProfileCache } from "./cache";

// REMOVE_BEFORE
const userId = "123";

exampleCache.evict(userId);
userProfileCache.evict(userId);
4 changes: 2 additions & 2 deletions packages/suspense-website/src/examples/createCache/hook.ts
Original file line number Diff line number Diff line change
@@ -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":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exampleCache } from "./cache";
import { userProfileCache } from "./cache";

function getQueryParam(key: string): string {
return "dummy";
Expand All @@ -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() {
// ...
Expand Down
Original file line number Diff line number Diff line change
@@ -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);

// ...
}
6 changes: 3 additions & 3 deletions packages/suspense-website/src/examples/createCache/sync.ts
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>, path: string) => {
let loadedLines = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string>,
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
);
9 changes: 9 additions & 0 deletions packages/suspense-website/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
),
Expand Down Expand Up @@ -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")
),
Expand Down
17 changes: 8 additions & 9 deletions packages/suspense-website/src/routes/createCache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,17 @@ export default function CreateCacheRoute() {
</a>
.)
</p>
<p>Implementing one of these caches requires two methods:</p>
<ul>
<li>
One to compute a <em>unique key</em> from cache parameters, and
</li>
<li>One to load the data</li>
</ul>
<p>
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:
</p>
<Code code={createCache.cache} />
<p>
If one of the cache key parameters can't be stringified, a second
method should also be provided to compute a unique key.
</p>
<Code code={createCache.cacheWithKey} />
</Block>
<Block>
<SubHeading title="Using a cache with suspense" />
Expand Down
19 changes: 10 additions & 9 deletions packages/suspense-website/src/routes/createStreamingCache.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ export default function CreateStreamingCacheRoute() {
it provides a subscription interface that can be used to re-render as
data incrementally loads.
</p>
<p>Implementing a streaming cache requires two methods:</p>
<ul>
<li>
One to compute a <em>unique key</em> from cache parameters, and
</li>
<li>
One to stream the data using an API like a <code>WebSocket</code>
</li>
</ul>
<p>
Like a regular cache, implementing a streaming cache typically only
requires a single method– to stream the data using an API like a{" "}
<code>WebSocket</code>.
</p>
<Code code={createStreamingCache.cache} />
<p>
A second method can be provided to compute the cache key if one of the
parameters can't be stringified.
</p>
<Code code={createStreamingCache.cacheWithKey} />
</Block>
<Block>
<SubHeading title="Using a streaming cache" />
Expand Down
29 changes: 13 additions & 16 deletions packages/suspense-website/src/suspense/ImportCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
}
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -159,6 +158,7 @@ export const {

return parsedTokens;
},
(code: string, language: Language) => `${code.length}-${language}`,
"SyntaxParsingCache"
);

Expand Down
3 changes: 3 additions & 0 deletions packages/suspense/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/suspense/src/cache/createCache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/suspense/src/cache/createCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { assertPendingRecord } from "../utils/assertPendingRecord";
import { isThennable } from "../utils/isThennable";

export function createCache<Params extends Array<any>, Value>(
getKey: (...params: Params) => string,
load: (...params: Params) => Thennable<Value> | Value,
getKey: (...params: Params) => string = defaultGetKey,
debugLabel?: string
): Cache<Params, Value> {
const recordMap = new Map<string, Record<Value>>();
Expand Down Expand Up @@ -192,3 +192,7 @@ export function createCache<Params extends Array<any>, Value>(
subscribeToStatus,
};
}

function defaultGetKey(...params: any[]): string {
return params.join(",");
}
2 changes: 1 addition & 1 deletion packages/suspense/src/cache/createStreamingCache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ describe("createStreamingCache", () => {
);

cache = createStreamingCache<[string], string, any>(
getCacheKey,
fetch,
getCacheKey,
"cache"
);
});
Expand Down
6 changes: 5 additions & 1 deletion packages/suspense/src/cache/createStreamingCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export function createStreamingCache<
Value,
AdditionalData = undefined
>(
getKey: (...params: Params) => string,
load: (notifier: StreamingProgressNotifier<Value>, ...params: Params) => void,
getKey: (...params: Params) => string = defaultGetKey,
debugLabel?: string
): StreamingCache<Params, Value, AdditionalData> {
const streamingValuesMap = new Map<
Expand Down Expand Up @@ -140,3 +140,7 @@ export function createStreamingCache<
stream,
};
}

function defaultGetKey(...params: any[]): string {
return params.join(",");
}
2 changes: 1 addition & 1 deletion packages/suspense/src/hooks/useCacheStatus.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
Expand Down
9 changes: 3 additions & 6 deletions packages/suspense/src/hooks/useStreamingValues.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down

1 comment on commit c5e2342

@vercel
Copy link

@vercel vercel bot commented on c5e2342 Feb 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

suspense – ./

suspense-git-main-bvaughn.vercel.app
suspense-bvaughn.vercel.app
suspense-npm.vercel.app

Please sign in to comment.