-
Notifications
You must be signed in to change notification settings - Fork 0
refactor(web): react-query and zustand instead of rtk #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📝 WalkthroughWalkthroughThis pull request migrates the application's state management from Redux (RTK Query) to React Query and Zustand. Redux-based store setup, API slices, and dispatched actions are replaced with React Query hooks and a Zustand UI store. Components are updated to use new data-fetching and cache-invalidation patterns, and the root provider is switched to QueryProvider with client-side hydration support. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/components/common/profileDropdown.tsx (1)
1-1:⚠️ Potential issue | 🔴 CriticalMissing
"use client"directive.This component uses React hooks (
useRouter,useInvalidateProfile,useProfile) but lacks the"use client"directive at the top. Next.js will throw a runtime error when trying to use hooks in what it interprets as a Server Component.🐛 Proposed fix
+"use client"; + import { logoutUser } from "@/actions/auth";apps/web/components/question/questionVoteButton.tsx (1)
104-116:⚠️ Potential issue | 🟠 MajorMissing rollback logic for failed optimistic updates.
If
voteOnQuestionActionfails, the optimistic cache updates persist, leaving the UI in an inconsistent state with the server. Consider capturing the previous state and rolling back on error.🛡️ Proposed fix with rollback
const handleVote = async (voteType: VoteType) => { if (isEmpty(user)) return router.push("/login"); const isRemoving = currentVote === voteType; const newVoteValue = isRemoving ? null : voteType === "like" ? 1 : -1; + // Capture previous state for rollback + const previousVote = cachedUserVote; + const previousQuestion = cachedQuestion; + // Optimistically update both caches updateUserVotesCache(newVoteValue); updateQuestionsCache(voteType); // Perform server action - await voteOnQuestionAction(questionId, isRemoving ? "remove" : voteType); + const result = await voteOnQuestionAction(questionId, isRemoving ? "remove" : voteType); + + // Rollback on failure + if (!result?.success) { + updateUserVotesCache(previousVote); + // Restore previous question counts if needed + queryClient.setQueriesData<{ + pages: PaginatedResponse[]; + pageParams: number[]; + }>({ queryKey: ["questions", "list"] }, (oldData) => { + if (!oldData || !previousQuestion) return oldData; + return { + ...oldData, + pages: oldData.pages.map((page) => ({ + ...page, + data: page.data?.map((q) => + q.id === questionId ? { ...q, likes: previousQuestion.likes, dislikes: previousQuestion.dislikes } : q + ), + })), + }; + }); + } };apps/web/components/question/infiniteQuestionList.tsx (1)
45-47:⚠️ Potential issue | 🟡 MinorAvoid optional chaining for React keys.
Using
question?.idas a key means ifquestionisundefined, the key becomesundefined, which causes React warnings and breaks reconciliation. Ifpage.datacan contain undefined items, filter them out instead:Proposed fix
- {questions.map((question) => ( - <QuestionCard key={question?.id} question={question} /> + {questions.map((question) => ( + <QuestionCard key={question.id} question={question} /> ))}Or if undefined items are possible in the response, filter them first on line 15:
- const questions = data?.pages?.flatMap((page) => page.data) ?? []; + const questions = data?.pages?.flatMap((page) => page.data).filter(Boolean) ?? [];
🤖 Fix all issues with AI agents
In `@apps/web/lib/api.ts`:
- Around line 34-36: The current fetch handling returns null on non-OK responses
and blindly calls res.json(), which hides errors from React Query and crashes on
204/non‑JSON responses; update the logic in the API fetch code that awaits
fetch(url, fetchOptions) so that if !res.ok you throw an Error (include status
and statusText) to surface failures to React Query, and before calling
res.json() check for empty responses (e.g., res.status === 204) or verify
Content-Type includes application/json and return null or an appropriate empty
value when there is no JSON payload. Ensure you modify the branch around the res
variable returned by fetch(url, fetchOptions) rather than swallowing errors.
- Around line 1-20: The code uses baseUrl (const baseUrl =
process.env.NEXT_PUBLIC_BASEURL) without guarding for undefined which yields
URLs like "undefined/…"; update the initialization and/or the request function
(request) to default baseUrl to an empty string or validate it and treat
absolute endpoints (starting with "http") specially: ensure baseUrl is set to a
safe default (e.g., '') or throw a clear error, and when constructing url in
request use the safe base or return the endpoint unchanged if it is already an
absolute URL so requests never build "undefined/…".
In `@apps/web/lib/providers/QueryProvider.tsx`:
- Around line 7-12: Replace the useState-based QueryClient creation in
QueryProvider with a module-level getQueryClient() helper that returns a new
QueryClient when isServer is true and a singleton instance in the browser;
implement getQueryClient() to import isServer from `@tanstack/react-query` and to
use queryClientConfig when constructing the client, then call getQueryClient()
inside QueryProvider (instead of useState(() => new QueryClient(...))) so the
QueryClient survives App Router retries and preserves cache.
In `@apps/web/lib/queries/questions.ts`:
- Around line 100-120: useQuestionById currently reads the react-query cache
synchronously and doesn't subscribe to updates, so it won't reflect optimistic
changes; change it to a reactive implementation similar to useVoteByQuestionId
by using useSyncExternalStore (or subscribing to queryClient.getQueryCache()) to
subscribe to cache updates and return the current snapshot for queryKey
["questions","list"]; specifically, update the useQuestionById function to
register a subscribe callback (via useSyncExternalStore or queryCache.subscribe)
that triggers when the questions list changes and compute the current cached
question by iterating pages (same logic that searches page.data.find(q => q.id
=== questionId)), ensuring the hook returns updated likes/dislikes after
optimistic updates from questionVoteButton.tsx.
- Around line 81-98: useVoteByQuestionId currently reads the react-query cache
synchronously and doesn't subscribe to updates, causing stale UI after
optimistic updates; change it to a reactive read by using useQuery (queryKey:
["userVotes"]) with a select function that returns data?.[questionId] as
-1|1|null so the hook subscribes to the userVotes cache and re-renders when
updateUserVotesCache (used in questionVoteButton.tsx) mutates the cache;
alternatively implement a useSyncExternalStore subscriber to the
queryClient.getQueryCache() keyed to "userVotes" that returns the specific
question vote to ensure components update on cache changes.
In `@apps/web/package.json`:
- Line 58: The package.json lists the devDependency
"@tanstack/eslint-plugin-query" but apps/web/eslint.config.js doesn't reference
it; either remove the dependency from package.json or add the plugin to the
ESLint config: import/register the plugin in apps/web/eslint.config.js (e.g.,
include "@tanstack/eslint-plugin-query" in the plugins/overrides or add its
recommended config to extends) and enable any desired rules from that plugin so
the dependency is actually used; update package.json only if you choose to
remove the unused entry.
🧹 Nitpick comments (8)
apps/web/lib/getQueryClient.ts (1)
5-16: Cache()-based singleton is officially supported but optional; be aware of serialization overhead.TanStack Query v5 docs explicitly document this pattern as optional for request-scoped QueryClient reuse. However, the primary recommendation is creating a fresh
QueryClientper Server Component that needs prefetching. The cache() approach has a known tradeoff: eachdehydrate(getQueryClient())may serialize the entire client cache, adding serialization overhead. If minimizing payload is a concern, consider the per-component pattern instead.apps/web/components/profile/imageUpload.tsx (1)
20-25: Consider adding error handling for failed uploads.The
upload()call can throw, which would leave the user without feedback. Consider wrapping in try/catch to show an error state.💡 Suggested improvement
+ try { await upload("public/" + file.name, file, { access: "public", handleUploadUrl: "/api/user/image", }); router.refresh(); invalidateProfile(); + } catch (error) { + console.error("Upload failed:", error); + // Consider adding user-facing error feedback + }apps/web/store/useMulti.ts (1)
4-9: Consider adding a note about the minimum keys requirement.The docstring is helpful, but the function accepts zero keys via the spread operator, which would return an empty object. This is likely fine but could be documented.
📝 Optional: Enhanced documentation
/** * Select multiple properties directly from a store with shallow comparison * * `@example` * const { deleteModal } = useMulti(useUIStore, "deleteModal"); + * const { a, b } = useMulti(useStore, "a", "b"); + * + * `@param` store - A zustand store created with `create()` + * `@param` keys - One or more state property keys to select */apps/web/lib/queries/questions.ts (1)
33-34: Consider exportingSearchResultandSearchResponsetypes.These types are defined but not exported. If they're needed elsewhere (e.g., for testing or type reuse), consider exporting them.
♻️ Export types if needed elsewhere
-type SearchResult = { label: string; value: string }; -type SearchResponse = { data: { title: string; slug: string }[] }; +export type SearchResult = { label: string; value: string }; +export type SearchResponse = { data: { title: string; slug: string }[] };apps/web/components/question/questionSearch.tsx (2)
15-17: Remove unnecessaryasynckeyword.The
handleChangefunction no longer performs any asynchronous operations after the migration. Theasynckeyword can be removed.♻️ Remove async
- const handleChange = async (search: string) => { + const handleChange = (search: string) => { setSearchValue(search); };
43-52: Use uniquekeyinstead of array index.Using
indexas a key can cause issues with React's reconciliation when items are reordered or filtered. Usequestion.value(the slug) which is unique.♻️ Use unique key
data?.map((question, index) => ( <Link - key={index} + key={question.value} className="p-3 " href={"/question/" + question?.value} onClick={onClick}apps/web/components/question/questionVoteButton.tsx (1)
20-20: DuplicateVoteTypedefinition.
VoteTypeis already defined inapps/web/data/questionVote.ts(including"remove"). Consider importing it to avoid duplication and ensure consistency.♻️ Import existing type
+import type { VoteType } from "@/data/questionVote"; // ... -type VoteType = "like" | "dislike";Note: The imported type includes
"remove", which is used inhandleVotewhen callingvoteOnQuestionAction.apps/web/components/question/questionCard.tsx (1)
16-16: Remove unuseduseUIStoreimport.Only
useUIStoreSelectoris used in this component. TheuseUIStoreimport is unused.♻️ Remove unused import
-import { useUIStore, useUIStoreSelector } from "@/store/useUIStore"; +import { useUIStoreSelector } from "@/store/useUIStore";
| const baseUrl = process.env.NEXT_PUBLIC_BASEURL; | ||
|
|
||
| type Params = Record<string, string | number | undefined>; | ||
|
|
||
| type FetchOptions = Omit<RequestInit, "body"> & { | ||
| params?: Params; | ||
| }; | ||
|
|
||
| type MutationOptions = FetchOptions & { | ||
| body?: unknown; | ||
| }; | ||
|
|
||
| async function request<T>( | ||
| endpoint: string, | ||
| options?: FetchOptions & { body?: BodyInit }, | ||
| ): Promise<T | null> { | ||
| const { params, ...fetchOptions } = options ?? {}; | ||
|
|
||
| let url = `${baseUrl}${endpoint}`; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard missing NEXT_PUBLIC_BASEURL to avoid “undefined” URLs.
If the env var is unset, all requests will go to undefined/.... Default to a safe empty base or handle absolute endpoints.
🔧 Proposed fix
-const baseUrl = process.env.NEXT_PUBLIC_BASEURL;
+const baseUrl = process.env.NEXT_PUBLIC_BASEURL ?? "";
@@
- let url = `${baseUrl}${endpoint}`;
+ let url = endpoint.startsWith("http")
+ ? endpoint
+ : `${baseUrl}${endpoint}`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const baseUrl = process.env.NEXT_PUBLIC_BASEURL; | |
| type Params = Record<string, string | number | undefined>; | |
| type FetchOptions = Omit<RequestInit, "body"> & { | |
| params?: Params; | |
| }; | |
| type MutationOptions = FetchOptions & { | |
| body?: unknown; | |
| }; | |
| async function request<T>( | |
| endpoint: string, | |
| options?: FetchOptions & { body?: BodyInit }, | |
| ): Promise<T | null> { | |
| const { params, ...fetchOptions } = options ?? {}; | |
| let url = `${baseUrl}${endpoint}`; | |
| const baseUrl = process.env.NEXT_PUBLIC_BASEURL ?? ""; | |
| type Params = Record<string, string | number | undefined>; | |
| type FetchOptions = Omit<RequestInit, "body"> & { | |
| params?: Params; | |
| }; | |
| type MutationOptions = FetchOptions & { | |
| body?: unknown; | |
| }; | |
| async function request<T>( | |
| endpoint: string, | |
| options?: FetchOptions & { body?: BodyInit }, | |
| ): Promise<T | null> { | |
| const { params, ...fetchOptions } = options ?? {}; | |
| let url = endpoint.startsWith("http") | |
| ? endpoint | |
| : `${baseUrl}${endpoint}`; | |
🤖 Prompt for AI Agents
In `@apps/web/lib/api.ts` around lines 1 - 20, The code uses baseUrl (const
baseUrl = process.env.NEXT_PUBLIC_BASEURL) without guarding for undefined which
yields URLs like "undefined/…"; update the initialization and/or the request
function (request) to default baseUrl to an empty string or validate it and
treat absolute endpoints (starting with "http") specially: ensure baseUrl is set
to a safe default (e.g., '') or throw a clear error, and when constructing url
in request use the safe base or return the endpoint unchanged if it is already
an absolute URL so requests never build "undefined/…".
| const res = await fetch(url, fetchOptions); | ||
| if (!res.ok) return null; | ||
| return await res.json(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t treat non‑OK responses as successful data.
Returning null on errors makes React Query treat failures as success and res.json() will throw on 204/non‑JSON responses.
🔧 Proposed fix
- const res = await fetch(url, fetchOptions);
- if (!res.ok) return null;
- return await res.json();
+ const res = await fetch(url, fetchOptions);
+ if (!res.ok) {
+ const text = await res.text().catch(() => "");
+ throw new Error(`Request failed (${res.status}) ${text}`);
+ }
+ if (res.status === 204) return null;
+ const contentType = res.headers.get("content-type") ?? "";
+ if (!contentType.includes("application/json")) return null;
+ return (await res.json()) as T;🤖 Prompt for AI Agents
In `@apps/web/lib/api.ts` around lines 34 - 36, The current fetch handling returns
null on non-OK responses and blindly calls res.json(), which hides errors from
React Query and crashes on 204/non‑JSON responses; update the logic in the API
fetch code that awaits fetch(url, fetchOptions) so that if !res.ok you throw an
Error (include status and statusText) to surface failures to React Query, and
before calling res.json() check for empty responses (e.g., res.status === 204)
or verify Content-Type includes application/json and return null or an
appropriate empty value when there is no JSON payload. Ensure you modify the
branch around the res variable returned by fetch(url, fetchOptions) rather than
swallowing errors.
| export default function QueryProvider({ children }: { children: ReactNode }) { | ||
| const [queryClient] = useState(() => new QueryClient(queryClientConfig)); | ||
|
|
||
| return ( | ||
| <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TanStack Query v5 QueryClient initialization pattern useState useMemo Next.js App Router best practice
💡 Result:
Best-practice (TanStack Query v5 + Next.js App Router)
Use a dedicated client Providers component and create the QueryClient via a getQueryClient() helper that:
- creates a new client on the server (per request), and
- reuses a module-level singleton in the browser (so it survives React retries/suspense during initial render). (tanstack.com)
// app/providers.tsx
'use client'
import { QueryClient, QueryClientProvider, isServer } from '@tanstack/react-query'
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000, // common SSR/default choice to avoid immediate refetch
},
},
})
}
let browserQueryClient: QueryClient | undefined
function getQueryClient() {
if (isServer) return makeQueryClient()
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
}
export function Providers({ children }: { children: React.ReactNode }) {
const queryClient = getQueryClient()
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
}// app/layout.tsx
import { Providers } from './providers'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}How this compares to useState / useMemo
useState(() => new QueryClient())is a common pattern in “pure client” setups (it’s even shown in TanStack’s SSR guide examples for non–App Router contexts). (tanstack.com)- For App Router + streaming/Suspense, TanStack’s Advanced Server Rendering guidance emphasizes ensuring the browser
QueryClientis not recreated if React suspends/retries during the initial render, which is why the module-level singleton +getQueryClient()pattern is recommended there. (tanstack.com)
If you tell me whether you’re doing server prefetch + dehydrate/HydrationBoundary (or purely client fetching), I can tailor the exact file layout and hydration wiring.
Citations:
- 1: https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr?utm_source=openai
- 2: https://tanstack.com/query/v5/docs/react/guides/ssr?utm_source=openai
- 3: https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr?utm_source=openai
Update QueryClient initialization to follow TanStack Query v5 App Router best practice.
The current useState-based initialization should be replaced with a getQueryClient() helper using a module-level singleton pattern with isServer check. This prevents QueryClient recreation during React suspense/retry in App Router and is the recommended pattern for Next.js App Router per TanStack Query v5 official guidance.
Recommended implementation:
- Create a
getQueryClient()helper that returns a new client on server and a reused singleton in browser - Use
isServerfrom@tanstack/react-queryto distinguish environments - This ensures the client survives React retries during initial render and preserves cache state
🤖 Prompt for AI Agents
In `@apps/web/lib/providers/QueryProvider.tsx` around lines 7 - 12, Replace the
useState-based QueryClient creation in QueryProvider with a module-level
getQueryClient() helper that returns a new QueryClient when isServer is true and
a singleton instance in the browser; implement getQueryClient() to import
isServer from `@tanstack/react-query` and to use queryClientConfig when
constructing the client, then call getQueryClient() inside QueryProvider
(instead of useState(() => new QueryClient(...))) so the QueryClient survives
App Router retries and preserves cache.
| export function useVoteByQuestionId(questionId: string) { | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| // Look through all userVotes queries to find the vote for this question | ||
| const queriesData = queryClient.getQueriesData<Record<string, number | null>>( | ||
| { | ||
| queryKey: ["userVotes"], | ||
| }, | ||
| ); | ||
|
|
||
| for (const [, data] of queriesData) { | ||
| if (data?.[questionId] !== undefined) { | ||
| return data[questionId] as -1 | 1 | null; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-reactive cache read will cause stale UI after optimistic updates.
useVoteByQuestionId reads from the query cache synchronously but doesn't subscribe to cache changes. When updateUserVotesCache in questionVoteButton.tsx updates the cache, components using this hook won't re-render because there's no subscription.
Consider using useQuery with select to create a reactive subscription, or use useSyncExternalStore for cache reads.
🐛 Proposed reactive implementation
export function useVoteByQuestionId(questionId: string) {
const queryClient = useQueryClient();
-
- // Look through all userVotes queries to find the vote for this question
- const queriesData = queryClient.getQueriesData<Record<string, number | null>>(
- {
- queryKey: ["userVotes"],
- },
- );
-
- for (const [, data] of queriesData) {
- if (data?.[questionId] !== undefined) {
- return data[questionId] as -1 | 1 | null;
- }
- }
-
- return null;
+
+ // Subscribe to cache changes using useSyncExternalStore
+ const vote = React.useSyncExternalStore(
+ (onStoreChange) => {
+ return queryClient.getQueryCache().subscribe(onStoreChange);
+ },
+ () => {
+ const queriesData = queryClient.getQueriesData<Record<string, number | null>>({
+ queryKey: ["userVotes"],
+ });
+ for (const [, data] of queriesData) {
+ if (data?.[questionId] !== undefined) {
+ return data[questionId] as -1 | 1 | null;
+ }
+ }
+ return null;
+ },
+ );
+
+ return vote;
}🤖 Prompt for AI Agents
In `@apps/web/lib/queries/questions.ts` around lines 81 - 98, useVoteByQuestionId
currently reads the react-query cache synchronously and doesn't subscribe to
updates, causing stale UI after optimistic updates; change it to a reactive read
by using useQuery (queryKey: ["userVotes"]) with a select function that returns
data?.[questionId] as -1|1|null so the hook subscribes to the userVotes cache
and re-renders when updateUserVotesCache (used in questionVoteButton.tsx)
mutates the cache; alternatively implement a useSyncExternalStore subscriber to
the queryClient.getQueryCache() keyed to "userVotes" that returns the specific
question vote to ensure components update on cache changes.
| // Helper hook to get a question from cache by ID | ||
| export function useQuestionById(questionId: string) { | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| const queriesData = queryClient.getQueriesData<{ | ||
| pages: PaginatedResponse[]; | ||
| }>({ | ||
| queryKey: ["questions", "list"], | ||
| }); | ||
|
|
||
| for (const [, data] of queriesData) { | ||
| if (data?.pages) { | ||
| for (const page of data.pages) { | ||
| const question = page.data?.find((q) => q.id === questionId); | ||
| if (question) return question; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same non-reactive issue as useVoteByQuestionId.
useQuestionById also reads synchronously from the cache without subscribing to updates. After optimistic updates in questionVoteButton.tsx, the cachedQuestion won't reflect the new likes/dislikes counts until a re-render is triggered by something else.
Apply the same useSyncExternalStore pattern or refactor to use a reactive query approach.
🤖 Prompt for AI Agents
In `@apps/web/lib/queries/questions.ts` around lines 100 - 120, useQuestionById
currently reads the react-query cache synchronously and doesn't subscribe to
updates, so it won't reflect optimistic changes; change it to a reactive
implementation similar to useVoteByQuestionId by using useSyncExternalStore (or
subscribing to queryClient.getQueryCache()) to subscribe to cache updates and
return the current snapshot for queryKey ["questions","list"]; specifically,
update the useQuestionById function to register a subscribe callback (via
useSyncExternalStore or queryCache.subscribe) that triggers when the questions
list changes and compute the current cached question by iterating pages (same
logic that searches page.data.find(q => q.id === questionId)), ensuring the hook
returns updated likes/dislikes after optimistic updates from
questionVoteButton.tsx.
| "devDependencies": { | ||
| "@repo/eslint-config": "workspace:*", | ||
| "@repo/typescript-config": "workspace:*", | ||
| "@tanstack/eslint-plugin-query": "^5.91.4", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find ESLint config files and verify `@tanstack/eslint-plugin-query` is registered
echo "=== Finding ESLint config files ==="
fd -H -a -t f 'eslint\.(config|rc|rc\.js|rc\.cjs|rc\.mjs|rc\.json|rc\.yaml|rc\.yml)$' . 2>/dev/null
echo ""
echo "=== Searching for tanstack/query or eslint-plugin-query references ==="
fd -H -a -t f 'eslint\.(config|rc|rc\.js|rc\.cjs|rc\.mjs|rc\.json|rc\.yaml|rc\.yml)$' . \
-x rg -n 'tanstack/query|eslint-plugin-query' {} 2>/dev/null
echo ""
echo "=== Also checking for eslint.config.js or eslintrc patterns with broader search ==="
fd -H -a -t f '\.(eslintrc|eslint\.config)\.(js|cjs|mjs|json|yaml|yml)$' . 2>/dev/nullRepository: akashdas99/undoubt-next
Length of output: 259
🏁 Script executed:
#!/bin/bash
# Check if eslintConfig is in package.json
echo "=== Checking for eslintConfig in package.json ==="
rg -n 'eslintConfig|eslint-plugin' apps/web/package.json 2>/dev/null
echo ""
echo "=== Looking for any eslint references in config files ==="
rg -l 'eslint' . --type json --type js --type yaml 2>/dev/null | head -20
echo ""
echo "=== Checking for .eslintrc without extension ==="
fd -H -a '.eslintrc' . 2>/dev/null
echo ""
echo "=== Full search for eslint-plugin in all text files ==="
rg 'eslint-plugin-query|tanstack.*eslint' . --type-not node_modules 2>/dev/null
echo ""
echo "=== Check next.config.js if it exists and has eslint config ==="
if [ -f "apps/web/next.config.js" ]; then
echo "Found next.config.js:"
cat -n apps/web/next.config.js | head -50
fiRepository: akashdas99/undoubt-next
Length of output: 1190
🏁 Script executed:
#!/bin/bash
# Read the eslint config file to see what plugins are configured
echo "=== Content of apps/web/eslint.config.js ==="
cat -n apps/web/eslint.config.js
echo ""
echo "=== Searching for 'plugins' in the eslint config ==="
rg -n 'plugins' apps/web/eslint.config.js -A 5 -B 2Repository: akashdas99/undoubt-next
Length of output: 701
Add @tanstack/eslint-plugin-query to the ESLint config or remove it from dependencies.
The plugin is installed in package.json but not configured in apps/web/eslint.config.js (only eslint-plugin-drizzle is configured). Either wire it into the config and apply its rules, or remove the unused devDependency.
🤖 Prompt for AI Agents
In `@apps/web/package.json` at line 58, The package.json lists the devDependency
"@tanstack/eslint-plugin-query" but apps/web/eslint.config.js doesn't reference
it; either remove the dependency from package.json or add the plugin to the
ESLint config: import/register the plugin in apps/web/eslint.config.js (e.g.,
include "@tanstack/eslint-plugin-query" in the plugins/overrides or add its
recommended config to extends) and enable any desired rules from that plugin so
the dependency is actually used; update package.json only if you choose to
remove the unused entry.
Summary by CodeRabbit
Release Notes
Refactor
Chores
✏️ Tip: You can customize this high-level summary in your review settings.