Param20h commited on
Commit
c1036d0
Β·
unverified Β·
1 Parent(s): bf52102

fix(lint): resolve react-hooks/set-state-in-effect errors in CI

Browse files

- auth.tsx: lazy-initialize token state from localStorage instead of setToken inside useEffect
- dashboard/page.tsx: wrap loadDocuments() in async IIFE so setState fires in callback not effect body
- PDFViewer.tsx: remove useEffect+setPageInput sync and unused token variable; initialize pageInput from prop directly

frontend/src/app/dashboard/page.tsx CHANGED
@@ -46,7 +46,11 @@ export default function DashboardPage() {
46
  }, []);
47
 
48
  useEffect(() => {
49
- if (user) loadDocuments();
 
 
 
 
50
  }, [user, loadDocuments]);
51
 
52
  // Poll for processing status
 
46
  }, []);
47
 
48
  useEffect(() => {
49
+ if (!user) return;
50
+ // Use IIFE so setState calls happen in async callback, not effect body directly
51
+ void (async () => {
52
+ await loadDocuments();
53
+ })();
54
  }, [user, loadDocuments]);
55
 
56
  // Poll for processing status
frontend/src/components/document/PDFViewer.tsx CHANGED
@@ -1,6 +1,6 @@
1
  "use client";
2
 
3
- import { useState, useEffect } from "react";
4
  import { Button } from "@/components/ui/button";
5
  import { Input } from "@/components/ui/input";
6
  import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from "lucide-react";
@@ -16,17 +16,13 @@ interface Props {
16
  export default function PDFViewer({ documentId, currentPage, onPageChange, totalPages }: Props) {
17
  const [scale, setScale] = useState(1.0);
18
  const [loading, setLoading] = useState(true);
 
 
 
19
  const [pageInput, setPageInput] = useState(String(currentPage));
20
-
21
- useEffect(() => {
22
- setPageInput(String(currentPage));
23
- }, [currentPage]);
24
-
25
- const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
26
  const pdfUrl = `${API_BASE}/api/v1/documents/${documentId}/pdf`;
27
 
28
- // Use an iframe to render the PDF with the browser's native viewer
29
- // We append a page fragment for navigation
30
  const iframeSrc = `${pdfUrl}#page=${currentPage}`;
31
 
32
  const handlePageSubmit = (e: React.FormEvent) => {
@@ -35,10 +31,12 @@ export default function PDFViewer({ documentId, currentPage, onPageChange, total
35
  if (!isNaN(num) && num >= 1 && num <= totalPages) {
36
  onPageChange(num);
37
  } else {
 
38
  setPageInput(String(currentPage));
39
  }
40
  };
41
 
 
42
  return (
43
  <div className="h-full flex flex-col bg-background">
44
  {/* ── Toolbar ─────────────────────────────────── */}
 
1
  "use client";
2
 
3
+ import { useState } from "react";
4
  import { Button } from "@/components/ui/button";
5
  import { Input } from "@/components/ui/input";
6
  import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from "lucide-react";
 
16
  export default function PDFViewer({ documentId, currentPage, onPageChange, totalPages }: Props) {
17
  const [scale, setScale] = useState(1.0);
18
  const [loading, setLoading] = useState(true);
19
+ // Local editable value β€” initialized from currentPage prop.
20
+ // The iframe key={documentId-currentPage} already forces remount on
21
+ // external page changes, so no useEffect sync is needed.
22
  const [pageInput, setPageInput] = useState(String(currentPage));
 
 
 
 
 
 
23
  const pdfUrl = `${API_BASE}/api/v1/documents/${documentId}/pdf`;
24
 
25
+ // Append page fragment for native viewer navigation
 
26
  const iframeSrc = `${pdfUrl}#page=${currentPage}`;
27
 
28
  const handlePageSubmit = (e: React.FormEvent) => {
 
31
  if (!isNaN(num) && num >= 1 && num <= totalPages) {
32
  onPageChange(num);
33
  } else {
34
+ // Reset to the current valid page without needing a useEffect
35
  setPageInput(String(currentPage));
36
  }
37
  };
38
 
39
+
40
  return (
41
  <div className="h-full flex flex-col bg-background">
42
  {/* ── Toolbar ─────────────────────────────────── */}
frontend/src/lib/auth.tsx CHANGED
@@ -24,26 +24,28 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
24
 
25
  export function AuthProvider({ children }: { children: React.ReactNode }) {
26
  const [user, setUser] = useState<User | null>(null);
27
- const [token, setToken] = useState<string | null>(null);
 
 
 
28
  const [loading, setLoading] = useState(true);
29
 
30
- // ── Load user on mount ────────────────────────────
31
  useEffect(() => {
32
- const saved = localStorage.getItem("token");
33
- if (saved) {
34
- setToken(saved);
35
- api
36
- .get<User>("/api/v1/auth/me", { token: saved })
37
- .then(setUser)
38
- .catch(() => {
39
- localStorage.removeItem("token");
40
- setToken(null);
41
- })
42
- .finally(() => setLoading(false));
43
- } else {
44
  setLoading(false);
 
45
  }
46
- }, []);
 
 
 
 
 
 
 
 
 
47
 
48
  const login = useCallback(async (email: string, password: string) => {
49
  const data = await api.post<{ access_token: string; user: User }>(
 
24
 
25
  export function AuthProvider({ children }: { children: React.ReactNode }) {
26
  const [user, setUser] = useState<User | null>(null);
27
+ // Lazy initializer reads localStorage once β€” avoids setState-in-effect lint error
28
+ const [token, setToken] = useState<string | null>(
29
+ () => (typeof window !== "undefined" ? localStorage.getItem("token") : null)
30
+ );
31
  const [loading, setLoading] = useState(true);
32
 
33
+ // ── Validate saved token on mount ─────────────────
34
  useEffect(() => {
35
+ if (!token) {
 
 
 
 
 
 
 
 
 
 
 
36
  setLoading(false);
37
+ return;
38
  }
39
+ api
40
+ .get<User>("/api/v1/auth/me", { token })
41
+ .then(setUser)
42
+ .catch(() => {
43
+ localStorage.removeItem("token");
44
+ setToken(null);
45
+ })
46
+ .finally(() => setLoading(false));
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, []); // intentionally runs once on mount only
49
 
50
  const login = useCallback(async (email: string, password: string) => {
51
  const data = await api.post<{ access_token: string; user: User }>(