diff --git a/src/app/(components)/cache/cache-provider.tsx b/src/app/(components)/cache/cache-provider.tsx
new file mode 100644
index 00000000..f60240e6
--- /dev/null
+++ b/src/app/(components)/cache/cache-provider.tsx
@@ -0,0 +1,26 @@
+"use client";
+import * as React from "react";
+
+export function RSCCacheProvider({ children }: { children: React.ReactNode }) {
+ const [cache] = React.useState(() => new Map());
+ return (
+ {children}
+ );
+}
+
+export type RSCCacheContextType = Map<
+ string,
+ Promise
+> | null;
+
+export const CacheContext = React.createContext(null);
+
+export function useRSCCacheContext() {
+ const cache = React.use(CacheContext);
+
+ if (!cache) {
+ throw new Error("Cache is null, this should never arrives");
+ }
+
+ return cache;
+}
diff --git a/src/app/(components)/cache/cache.client.tsx b/src/app/(components)/cache/cache.client.tsx
index d40e4d93..0b26ff4a 100644
--- a/src/app/(components)/cache/cache.client.tsx
+++ b/src/app/(components)/cache/cache.client.tsx
@@ -5,6 +5,7 @@ import * as RSDW from "react-server-dom-webpack/client";
import { getSSRManifest } from "~/app/(components)/cache/manifest";
import { ErrorBoundary } from "react-error-boundary";
+import { useRSCCacheContext } from "~/app/(components)/cache/cache-provider";
function transformStringToStream(input: string) {
// Using Flight to deserialize the args from the string.
@@ -21,7 +22,13 @@ function transformStringToStream(input: string) {
});
}
-export function CacheClient({ payload }: { payload: string }) {
+export function CacheClient({
+ payload,
+ cacheKey
+}: {
+ payload: string;
+ cacheKey: string;
+}) {
if (typeof window === "undefined") {
console.time("running cache client for SSR...");
console.log("[SSR] before `use`");
@@ -29,7 +36,29 @@ export function CacheClient({ payload }: { payload: string }) {
console.log("[CSR] before `use`");
console.time("running cache client for CSR...");
}
- const element = React.use(resolveElementCached(payload));
+
+ let rscPromise: Promise | null = null;
+ const rscCache = useRSCCacheContext();
+ if (rscCache.has(cacheKey)) {
+ rscPromise = rscCache.get(cacheKey)!;
+ } else {
+ const rscStream = transformStringToStream(payload);
+ // Render to HTML
+ if (typeof window === "undefined") {
+ // the SSR manifest contains all the client components that will be SSR'ed
+ // And also how to import them
+ rscPromise = RSDWSSr.createFromReadableStream(
+ rscStream,
+ getSSRManifest()
+ );
+ } else {
+ // Hydrate or CSR
+ rscPromise = RSDW.createFromReadableStream(rscStream, {});
+ }
+ rscCache.set(cacheKey, rscPromise);
+ }
+
+ const element = React.use(rscPromise);
if (typeof window === "undefined") {
console.log("[SSR] after `use`");
console.timeEnd("running cache client for SSR...");
@@ -40,47 +69,6 @@ export function CacheClient({ payload }: { payload: string }) {
return element;
}
-/**
- * Custom `cache` function as `React.cache` doesn't work in the client
- * @param fn
- * @returns
- */
-function fnCache any>(fn: T): T {
- const cache = new Map();
-
- return function cachedFn(...args: Parameters): ReturnType {
- const key = JSON.stringify(args);
- if (cache.has(key)) {
- return cache.get(key);
- }
-
- const result = fn(...args);
- cache.set(key, result);
- return result;
- } as T;
-}
-
-const resolveElementCached = fnCache(async function resolveElementCached(
- payload: string
-) {
- const rscStream = transformStringToStream(payload);
- let rscPromise: Promise | null = null;
-
- // Render to HTML
- if (typeof window === "undefined") {
- // the SSR manifest contains all the client components that will be SSR'ed
- // And also how to import them
- rscPromise = RSDWSSr.createFromReadableStream(rscStream, getSSRManifest());
- }
-
- // Hydrate or CSR
- if (rscPromise === null) {
- rscPromise = RSDW.createFromReadableStream(rscStream, {});
- }
-
- return await rscPromise;
-});
-
export function CacheErrorBoundary({
children,
fallback
diff --git a/src/app/(components)/cache/cache.server.tsx b/src/app/(components)/cache/cache.server.tsx
index 2f8968ed..e9862b6b 100644
--- a/src/app/(components)/cache/cache.server.tsx
+++ b/src/app/(components)/cache/cache.server.tsx
@@ -94,12 +94,14 @@ export async function Cache({
}
if (debug) {
- return {cachedPayload.rsc}
;
+ return (
+ {cachedPayload.rsc}
+ );
}
return (
>}>
-
+
);
} catch (error) {
diff --git a/src/app/(components)/markdown/markdown-h.tsx b/src/app/(components)/markdown/markdown-h.tsx
index 30195eeb..b6d249ba 100644
--- a/src/app/(components)/markdown/markdown-h.tsx
+++ b/src/app/(components)/markdown/markdown-h.tsx
@@ -30,7 +30,9 @@ export function MarkdownH({ as, showLink, ...props }: MarkdownHProps) {
href={`#${props.id}`}
className={clsx(
"absolute -left-6 -top-1.5 opacity-100 transition duration-150",
- "md:opacity-0 md:group-hover:opacity-100"
+ "md:opacity-0 md:group-hover:opacity-100",
+ "focus:opacity-100 focus:ring-2 focus:ring-accent focus:outline-none",
+ "rounded-md"
)}
>
diff --git a/src/app/(components)/providers.tsx b/src/app/(components)/providers.tsx
index fd25b5a8..2af2bb8d 100644
--- a/src/app/(components)/providers.tsx
+++ b/src/app/(components)/providers.tsx
@@ -4,6 +4,7 @@ import * as React from "react";
// components
import { RouterProvider } from "react-aria-components";
import { ReactQueryProvider } from "~/app/(components)/react-query-provider";
+import { RSCCacheProvider } from "~/app/(components)/cache/cache-provider";
// utils
import { useRouter } from "next/navigation";
@@ -13,7 +14,9 @@ export function ClientProviders({ children }: { children: React.ReactNode }) {
return (
- {children}
+
+ {children}
+
);
}
diff --git a/src/lib/server/rsc-utils.server.ts b/src/lib/server/rsc-utils.server.ts
index c0237859..763b5972 100644
--- a/src/lib/server/rsc-utils.server.ts
+++ b/src/lib/server/rsc-utils.server.ts
@@ -17,6 +17,9 @@ export function nextCache(
return cache(unstable_cache(cb, options.tags, options));
}
+/**
+ * This function is only used in `DEV` because fetch-cache is bypassed by nextjs on DEV
+ */
function cacheForDev(
cb: T,
options: {
@@ -30,16 +33,12 @@ function cacheForDev(
cached: ReturnType;
}>(key);
- if (!cachedValue) {
- cachedValue = await cb(...args);
- await kv.set(
- key,
- {
- cached: cachedValue
- },
- options.revalidate
- );
+ if (!cachedValue?.cached) {
+ cachedValue = {
+ cached: await cb(...args)
+ };
+ await kv.set(key, cachedValue, options.revalidate);
}
- return cachedValue!.cached;
+ return cachedValue.cached;
};
}