From a07dada0c9ba80976692ee14e256da0a2d6b0112 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Sun, 1 Feb 2026 10:16:35 +0900
Subject: refactor(atoms): migrate to jotai-tanstack-query from custom
 reloadable atoms

Replace custom createReloadableAtom/createReloadableAtomFamily with
atomWithSuspenseQuery from jotai-tanstack-query, leveraging TanStack
Query's built-in caching, invalidation, and Suspense support.

- Add @tanstack/query-core and jotai-tanstack-query dependencies
- Create shared QueryClient instance (src/client/queryClient.ts)
- Migrate all data atoms (decks, cards, study, noteTypes) to atomWithSuspenseQuery
- Update page components to use .data destructuring and queryClient.invalidateQueries()
- Update all test files to use QueryClient for data hydration
- Remove src/client/atoms/utils.ts (no longer needed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---
 src/client/pages/NoteTypesPage.test.tsx | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

(limited to 'src/client/pages/NoteTypesPage.test.tsx')

diff --git a/src/client/pages/NoteTypesPage.test.tsx b/src/client/pages/NoteTypesPage.test.tsx
index 8bacd0f..ac68e35 100644
--- a/src/client/pages/NoteTypesPage.test.tsx
+++ b/src/client/pages/NoteTypesPage.test.tsx
@@ -2,14 +2,15 @@
  * @vitest-environment jsdom
  */
 import "fake-indexeddb/auto";
+import { QueryClient } from "@tanstack/query-core";
 import { cleanup, render, screen, waitFor } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import { createStore, Provider } from "jotai";
+import { queryClientAtom } from "jotai-tanstack-query";
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 import { Router } from "wouter";
 import { memoryLocation } from "wouter/memory-location";
-import { authLoadingAtom, type NoteType, noteTypesAtom } from "../atoms";
-import { clearAtomFamilyCaches } from "../atoms/utils";
+import { authLoadingAtom, type NoteType } from "../atoms";
 import { NoteTypesPage } from "./NoteTypesPage";
 
 interface RenderOptions {
@@ -59,6 +60,14 @@ vi.mock("../api/client", () => ({
 	},
 }));
 
+// Mock queryClient module so pages use our test queryClient
+let testQueryClient: QueryClient;
+vi.mock("../queryClient", () => ({
+	get queryClient() {
+		return testQueryClient;
+	},
+}));
+
 import { ApiClientError, apiClient } from "../api/client";
 
 const mockNoteTypes = [
@@ -89,10 +98,11 @@ function renderWithProviders({
 	const { hook } = memoryLocation({ path });
 	const store = createStore();
 	store.set(authLoadingAtom, false);
+	store.set(queryClientAtom, testQueryClient);
 
-	// Hydrate atom if initial data provided
+	// Seed query cache if initial data provided
 	if (initialNoteTypes !== undefined) {
-		store.set(noteTypesAtom, initialNoteTypes);
+		testQueryClient.setQueryData(["noteTypes"], initialNoteTypes);
 	}
 
 	return render(
@@ -107,6 +117,11 @@ function renderWithProviders({
 describe("NoteTypesPage", () => {
 	beforeEach(() => {
 		vi.clearAllMocks();
+		testQueryClient = new QueryClient({
+			defaultOptions: {
+				queries: { staleTime: Number.POSITIVE_INFINITY, retry: false },
+			},
+		});
 		vi.mocked(apiClient.getTokens).mockReturnValue({
 			accessToken: "access-token",
 			refreshToken: "refresh-token",
@@ -138,7 +153,7 @@ describe("NoteTypesPage", () => {
 	afterEach(() => {
 		cleanup();
 		vi.restoreAllMocks();
-		clearAtomFamilyCaches();
+		testQueryClient.clear();
 	});
 
 	it("renders page title and back button", () => {
-- 
cgit v1.3-1-g0d28

