From 1fd719536125e7f6423364c27aef49d9331b17c5 Mon Sep 17 00:00:00 2001 From: hfellerhoff Date: Sun, 24 Aug 2025 17:23:20 -0400 Subject: [PATCH 1/4] add data persistence --- package.json | 2 ++ pnpm-lock.yaml | 32 ++++++++++++++++++++++++++++ src/trpc/react.tsx | 52 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 2ad41c1..3d69452 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,9 @@ "@sendgrid/mail": "^8.1.5", "@t3-oss/env-nextjs": "^0.13.8", "@tabler/icons-react": "^3.34.1", + "@tanstack/query-async-storage-persister": "^5.85.5", "@tanstack/react-query": "^5.85.5", + "@tanstack/react-query-persist-client": "^5.85.5", "@tanstack/react-table": "^8.21.3", "@trpc/client": "11.5.0", "@trpc/next": "11.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f88dad8..da9cab8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,9 +108,15 @@ importers: '@tabler/icons-react': specifier: ^3.34.1 version: 3.34.1(react@19.1.1) + '@tanstack/query-async-storage-persister': + specifier: ^5.85.5 + version: 5.85.5 '@tanstack/react-query': specifier: ^5.85.5 version: 5.85.5(react@19.1.1) + '@tanstack/react-query-persist-client': + specifier: ^5.85.5 + version: 5.85.5(@tanstack/react-query@5.85.5(react@19.1.1))(react@19.1.1) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -2608,9 +2614,21 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + '@tanstack/query-async-storage-persister@5.85.5': + resolution: {integrity: sha512-E1N+eMPWfV0PwTNa8tRqyOgIzFJSGvrC5hVIxNehLL/jucPvLi0QUlIG/KC4Vg6jVarONSLhONCM4dkSugEUFw==} + '@tanstack/query-core@5.85.5': resolution: {integrity: sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w==} + '@tanstack/query-persist-client-core@5.85.5': + resolution: {integrity: sha512-2JQiyiTVaaUu8pwPqOp6tjNa64ZN+0T9eZ3lfksV4le1VuG99fTcAYmZFIydvzwWlSM7GEF/1kpl5bwW2Y1qfQ==} + + '@tanstack/react-query-persist-client@5.85.5': + resolution: {integrity: sha512-KzISZPtJtWZAwH/Ln1FclaiHVwdeV04WX7wUYLe1vw7zyfcPljHeyXlmVf8nxhFm8ujMBdGQVzP2iNn6ehzjQA==} + peerDependencies: + '@tanstack/react-query': ^5.85.5 + react: ^18 || ^19 + '@tanstack/react-query@5.85.5': resolution: {integrity: sha512-/X4EFNcnPiSs8wM2v+b6DqS5mmGeuJQvxBglmDxl6ZQb5V26ouD2SJYAcC3VjbNwqhY2zjxVD15rDA5nGbMn3A==} peerDependencies: @@ -8949,8 +8967,22 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.1.12 + '@tanstack/query-async-storage-persister@5.85.5': + dependencies: + '@tanstack/query-persist-client-core': 5.85.5 + '@tanstack/query-core@5.85.5': {} + '@tanstack/query-persist-client-core@5.85.5': + dependencies: + '@tanstack/query-core': 5.85.5 + + '@tanstack/react-query-persist-client@5.85.5(@tanstack/react-query@5.85.5(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/query-persist-client-core': 5.85.5 + '@tanstack/react-query': 5.85.5(react@19.1.1) + react: 19.1.1 + '@tanstack/react-query@5.85.5(react@19.1.1)': dependencies: '@tanstack/query-core': 5.85.5 diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx index e85b2e7..a568a03 100644 --- a/src/trpc/react.tsx +++ b/src/trpc/react.tsx @@ -1,18 +1,40 @@ "use client"; import type { QueryClient } from "@tanstack/react-query"; -import { useState } from "react"; -import { QueryClientProvider } from "@tanstack/react-query"; import { createTRPCClient, loggerLink, unstable_httpBatchStreamLink, } from "@trpc/client"; import { createTRPCContext } from "@trpc/tanstack-react-query"; +import { useState } from "react"; import SuperJSON from "superjson"; -import { createQueryClient } from "./query-client"; import type { AppRouter } from "~/server/api/root"; +import { createQueryClient } from "./query-client"; + +import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persister"; +import { + Persister, + PersistQueryClientProvider, +} from "@tanstack/react-query-persist-client"; + +const createSerialAsyncStoragePersister = () => { + return createAsyncStoragePersister({ + storage: window.localStorage, + }); +}; + +let asyncStoragePersister: Persister; +export const getAsyncStoragePersister = () => { + if (typeof window === "undefined") { + // Server: do not use persister + return undefined; + } else { + // Browser: use singleton pattern to keep the same query client + return (asyncStoragePersister ??= createSerialAsyncStoragePersister()); + } +}; let clientQueryClientSingleton: QueryClient | undefined = undefined; export const getQueryClient = () => { @@ -25,7 +47,15 @@ export const getQueryClient = () => { } }; -export const { useTRPC, TRPCProvider } = createTRPCContext(); +export const { useTRPC, useTRPCClient, TRPCProvider } = + createTRPCContext(); + +const getBaseUrl = () => { + if (typeof window !== "undefined") return window.location.origin; + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; + // eslint-disable-next-line no-restricted-properties + return `http://localhost:${process.env.PORT ?? 3000}`; +}; export function TRPCReactProvider(props: { children: React.ReactNode }) { const queryClient = getQueryClient(); @@ -52,17 +82,13 @@ export function TRPCReactProvider(props: { children: React.ReactNode }) { ); return ( - + {props.children} - + ); } - -const getBaseUrl = () => { - if (typeof window !== "undefined") return window.location.origin; - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; - // eslint-disable-next-line no-restricted-properties - return `http://localhost:${process.env.PORT ?? 3000}`; -}; From b253d645f50bc71649948971b89ec04b44a9e78d Mon Sep 17 00:00:00 2001 From: hfellerhoff Date: Sun, 24 Aug 2025 17:26:36 -0400 Subject: [PATCH 2/4] remove server branch --- src/trpc/react.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx index a568a03..9409cb3 100644 --- a/src/trpc/react.tsx +++ b/src/trpc/react.tsx @@ -27,13 +27,7 @@ const createSerialAsyncStoragePersister = () => { let asyncStoragePersister: Persister; export const getAsyncStoragePersister = () => { - if (typeof window === "undefined") { - // Server: do not use persister - return undefined; - } else { - // Browser: use singleton pattern to keep the same query client - return (asyncStoragePersister ??= createSerialAsyncStoragePersister()); - } + return (asyncStoragePersister ??= createSerialAsyncStoragePersister()); }; let clientQueryClientSingleton: QueryClient | undefined = undefined; From 25efb2943dd2cc6e71462a6159d6f9de0292d9a8 Mon Sep 17 00:00:00 2001 From: hfellerhoff Date: Sun, 24 Aug 2025 17:36:25 -0400 Subject: [PATCH 3/4] fix warnings --- src/lib/auth-client.ts | 1 - src/trpc/react.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts index f279816..cce514d 100644 --- a/src/lib/auth-client.ts +++ b/src/lib/auth-client.ts @@ -1,5 +1,4 @@ import { createAuthClient } from "better-auth/react"; -import { env } from "~/env"; export const authClient = createAuthClient({}); diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx index 9409cb3..bf25f82 100644 --- a/src/trpc/react.tsx +++ b/src/trpc/react.tsx @@ -15,7 +15,7 @@ import { createQueryClient } from "./query-client"; import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persister"; import { - Persister, + type Persister, PersistQueryClientProvider, } from "@tanstack/react-query-persist-client"; From 5f0150bec79f810ff115637dcca323ce0663878c Mon Sep 17 00:00:00 2001 From: hfellerhoff Date: Tue, 26 Aug 2025 23:19:24 -0400 Subject: [PATCH 4/4] fix --- src/trpc/react.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx index bf25f82..26f6814 100644 --- a/src/trpc/react.tsx +++ b/src/trpc/react.tsx @@ -21,12 +21,15 @@ import { const createSerialAsyncStoragePersister = () => { return createAsyncStoragePersister({ - storage: window.localStorage, + storage: typeof window === "undefined" ? undefined : window.localStorage, }); }; let asyncStoragePersister: Persister; export const getAsyncStoragePersister = () => { + if (typeof window === "undefined") { + return asyncStoragePersister; + } return (asyncStoragePersister ??= createSerialAsyncStoragePersister()); };