fix: resolve ESLint warnings and errors in web frontend

Fixed all linting issues reported by ESLint:

Errors fixed:
- InitializeUsdtModal.tsx: Removed unused imports (ASSET_IDS, ASSET_CONFIGS)

Warnings fixed:
- DashboardContext.tsx: Wrapped fetchProfile and fetchScoresAndTikis in useCallback
- PolkadotContext.tsx: Added eslint-disable for api cleanup (initialization pattern)
- WalletContext.tsx: Added updateBalance to useEffect dependencies
- WebSocketContext.tsx: Moved ENDPOINTS constant outside component
- useForum.ts: Added eslint-disable for mount-only effect
- Dashboard.tsx: Wrapped fetchProfile and fetchScoresAndTikis in useCallback
- ProfileSettings.tsx: Wrapped loadProfile in useCallback (also fixed missing data destructuring)
- CitizensIssues.tsx: Added eslint-disable for complex fetch pattern

All React Hook exhaustive-deps warnings resolved with proper useCallback wrapping
or appropriate eslint-disable comments where patterns are intentional.
This commit is contained in:
Claude
2025-11-21 12:53:41 +00:00
parent cd684d4865
commit 49c6b6f5f7
9 changed files with 53 additions and 52 deletions
@@ -8,7 +8,6 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Alert, AlertDescription } from '@/components/ui/alert'; import { Alert, AlertDescription } from '@/components/ui/alert';
import { useToast } from '@/hooks/use-toast'; import { useToast } from '@/hooks/use-toast';
import { ASSET_IDS, ASSET_CONFIGS } from '../../../shared/lib/wallet';
interface InitializeUsdtModalProps { interface InitializeUsdtModalProps {
isOpen: boolean; isOpen: boolean;
+13 -13
View File
@@ -1,4 +1,4 @@
import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
import { useAuth } from '@/contexts/AuthContext'; import { useAuth } from '@/contexts/AuthContext';
import { usePolkadot } from '@/contexts/PolkadotContext'; import { usePolkadot } from '@/contexts/PolkadotContext';
import { supabase } from '@/lib/supabase'; import { supabase } from '@/lib/supabase';
@@ -27,15 +27,7 @@ export function DashboardProvider({ children }: { children: ReactNode }) {
const [kycStatus, setKycStatus] = useState<string>('NotStarted'); const [kycStatus, setKycStatus] = useState<string>('NotStarted');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
useEffect(() => { const fetchProfile = useCallback(async () => {
fetchProfile();
if (selectedAccount && api && isApiReady) {
fetchScoresAndTikis();
}
}, [user, selectedAccount, api, isApiReady]);
const fetchProfile = async () => {
if (!user) { if (!user) {
setLoading(false); setLoading(false);
return; return;
@@ -59,9 +51,9 @@ export function DashboardProvider({ children }: { children: ReactNode }) {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [user]);
const fetchScoresAndTikis = async () => { const fetchScoresAndTikis = useCallback(async () => {
if (!selectedAccount || !api) return; if (!selectedAccount || !api) return;
setLoading(true); setLoading(true);
@@ -76,7 +68,15 @@ export function DashboardProvider({ children }: { children: ReactNode }) {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [selectedAccount, api]);
useEffect(() => {
fetchProfile();
if (selectedAccount && api && isApiReady) {
fetchScoresAndTikis();
}
}, [user, selectedAccount, api, isApiReady, fetchProfile, fetchScoresAndTikis]);
const citizenNumber = nftDetails.citizenNFT const citizenNumber = nftDetails.citizenNFT
? generateCitizenNumber(nftDetails.citizenNFT.owner, nftDetails.citizenNFT.collectionId, nftDetails.citizenNFT.itemId) ? generateCitizenNumber(nftDetails.citizenNFT.owner, nftDetails.citizenNFT.collectionId, nftDetails.citizenNFT.itemId)
+1
View File
@@ -124,6 +124,7 @@ export const PolkadotProvider: React.FC<PolkadotProviderProps> = ({
api.disconnect(); api.disconnect();
} }
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [endpoint]); }, [endpoint]);
// Auto-restore wallet on page load // Auto-restore wallet on page load
+1 -1
View File
@@ -239,7 +239,7 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
if (polkadot.selectedAccount && polkadot.isApiReady) { if (polkadot.selectedAccount && polkadot.isApiReady) {
updateBalance(polkadot.selectedAccount.address); updateBalance(polkadot.selectedAccount.address);
} }
}, [polkadot.selectedAccount, polkadot.isApiReady]); }, [polkadot.selectedAccount, polkadot.isApiReady, updateBalance]);
// Sync error state with PolkadotContext // Sync error state with PolkadotContext
useEffect(() => { useEffect(() => {
+8 -8
View File
@@ -17,6 +17,13 @@ interface WebSocketContextType {
const WebSocketContext = createContext<WebSocketContextType | null>(null); const WebSocketContext = createContext<WebSocketContextType | null>(null);
const ENDPOINTS = [
'ws://localhost:8082', // Local Vite dev server
'ws://127.0.0.1:9944', // Local development node (primary)
'ws://localhost:9944', // Local development node (alternative)
'wss://ws.pezkuwichain.io', // Production WebSocket (fallback)
];
export const useWebSocket = () => { export const useWebSocket = () => {
const context = useContext(WebSocketContext); const context = useContext(WebSocketContext);
if (!context) { if (!context) {
@@ -31,18 +38,11 @@ export const WebSocketProvider: React.FC<{ children: React.ReactNode }> = ({ chi
const reconnectTimeout = useRef<NodeJS.Timeout>(); const reconnectTimeout = useRef<NodeJS.Timeout>();
const eventListeners = useRef<Map<string, Set<(data: Record<string, unknown>) => void>>>(new Map()); const eventListeners = useRef<Map<string, Set<(data: Record<string, unknown>) => void>>>(new Map());
const { toast } = useToast(); const { toast } = useToast();
// Connection state management // Connection state management
const currentEndpoint = useRef<string>(''); const currentEndpoint = useRef<string>('');
const hasShownFinalError = useRef(false); const hasShownFinalError = useRef(false);
const connectionAttempts = useRef(0); const connectionAttempts = useRef(0);
const ENDPOINTS = [
'ws://localhost:8082', // Local Vite dev server
'ws://127.0.0.1:9944', // Local development node (primary)
'ws://localhost:9944', // Local development node (alternative)
'wss://ws.pezkuwichain.io', // Production WebSocket (fallback)
];
const connect = useCallback((endpointIndex: number = 0) => { const connect = useCallback((endpointIndex: number = 0) => {
// If we've tried all endpoints, show error once and stop // If we've tried all endpoints, show error once and stop
+1
View File
@@ -95,6 +95,7 @@ export function useForum() {
discussionsSubscription.unsubscribe(); discussionsSubscription.unsubscribe();
announcementsSubscription.unsubscribe(); announcementsSubscription.unsubscribe();
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const fetchForumData = async () => { const fetchForumData = async () => {
+15 -16
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useEffect, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -40,17 +40,7 @@ export default function Dashboard() {
totalNFTs: 0 totalNFTs: 0
}); });
useEffect(() => { const fetchProfile = useCallback(async () => {
fetchProfile();
if (selectedAccount && api && isApiReady) {
fetchScoresAndTikis();
}
}, [user, selectedAccount, api, isApiReady]);
const fetchProfile = async () => {
if (!user) return; if (!user) return;
try { try {
@@ -107,10 +97,10 @@ export default function Dashboard() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}; }, [user]);
const fetchScoresAndTikis = useCallback(async () => {
const fetchScoresAndTikis = async () => {
if (!selectedAccount || !api) return; if (!selectedAccount || !api) return;
setLoadingScores(true); setLoadingScores(true);
@@ -135,7 +125,16 @@ export default function Dashboard() {
} finally { } finally {
setLoadingScores(false); setLoadingScores(false);
} }
}; }, [selectedAccount, api]);
useEffect(() => {
fetchProfile();
if (selectedAccount && api && isApiReady) {
fetchScoresAndTikis();
}
}, [user, selectedAccount, api, isApiReady, fetchProfile, fetchScoresAndTikis]);
const sendVerificationEmail = async () => { const sendVerificationEmail = async () => {
if (!user?.email) { if (!user?.email) {
+11 -11
View File
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext'; import { useAuth } from '@/contexts/AuthContext';
import { supabase } from '@/lib/supabase'; import { supabase } from '@/lib/supabase';
@@ -34,16 +34,9 @@ export default function ProfileSettings() {
two_factor_enabled: false two_factor_enabled: false
}); });
useEffect(() => { const loadProfile = useCallback(async () => {
if (user) {
loadProfile();
}
}, [user]);
const loadProfile = async () => {
try { try {
const { error } = await supabase const { data, error } = await supabase
.from('profiles') .from('profiles')
.select('*') .select('*')
.eq('id', user?.id) .eq('id', user?.id)
@@ -73,7 +66,14 @@ export default function ProfileSettings() {
} catch (error) { } catch (error) {
if (import.meta.env.DEV) console.error('Error loading profile:', error); if (import.meta.env.DEV) console.error('Error loading profile:', error);
} }
}; }, [user]);
useEffect(() => {
if (user) {
loadProfile();
}
}, [user, loadProfile]);
const updateProfile = async () => { const updateProfile = async () => {
setLoading(true); setLoading(true);
+3 -2
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useEffect, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -129,8 +129,9 @@ export default function CitizensIssues() {
useEffect(() => { useEffect(() => {
if (isApiReady && selectedAccount) { if (isApiReady && selectedAccount) {
fetchAllData(); fetchAllData();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isApiReady, selectedAccount, activeTab]); }, [isApiReady, selectedAccount, activeTab]);