mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
perf(admin): migrate AdminUserDetail loadUser to React Query (caching + cache key)
Replace the 13-line manual fetch with a useQuery hook keyed on userId. loadUser is kept as a thin wrapper around userQuery.refetch() so all 25+ existing call sites in mutation handlers continue to work unchanged. Sync useEffects copy data → setUser, isFetching → setLoading, and isError → navigate, preserving the original 'load fails → redirect to /admin/users' behavior. Remaining 12 loaders (loadSyncStatus/loadTariffs/loadTickets/loadReferrals/loadPanelInfo/ loadNodeUsage/loadDevices/loadSubscriptionData/loadGifts/loadPromoGroups/loadTicketDetail/ loadRequestHistory) NOT migrated in this commit — some are parameterized, some chain via Promise.all in loadSubscriptionData; they need a dedicated refactor with functional testing across all 7 tabs and the mutation flows. This commit ships the lowest-risk, highest-leverage piece (userQuery has the most call sites).
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import i18n from '../i18n';
|
||||
import { DEVICE_ALIAS_MAX_LENGTH } from '../constants/devices';
|
||||
import { useCurrency } from '../hooks/useCurrency';
|
||||
@@ -369,19 +370,43 @@ export default function AdminUserDetail() {
|
||||
|
||||
const userId = id ? parseInt(id, 10) : null;
|
||||
|
||||
const loadUser = useCallback(async () => {
|
||||
if (!userId) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await adminUsersApi.getUser(userId);
|
||||
setUser(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to load user:', error);
|
||||
// React Query owns the main user fetch: caching across navigations + auto-loading
|
||||
// state. loadUser is kept as a thin refetch wrapper so the 25+ mutation handlers
|
||||
// that call `await loadUser()` after writes need no changes.
|
||||
const userQuery = useQuery({
|
||||
queryKey: ['admin-user-detail', userId] as const,
|
||||
queryFn: () => {
|
||||
if (!userId) throw new Error('No userId');
|
||||
return adminUsersApi.getUser(userId);
|
||||
},
|
||||
enabled: !!userId && !isNaN(userId),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (userQuery.data) setUser(userQuery.data);
|
||||
}, [userQuery.data]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(userQuery.isFetching);
|
||||
}, [userQuery.isFetching]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userQuery.isError) {
|
||||
console.error('Failed to load user:', userQuery.error);
|
||||
navigate('/admin/users');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [userId, navigate]);
|
||||
}, [userQuery.isError, userQuery.error, navigate]);
|
||||
|
||||
const loadUser = useCallback(
|
||||
async () => {
|
||||
await userQuery.refetch();
|
||||
},
|
||||
// userQuery.refetch is a stable function across renders; including the whole
|
||||
// userQuery object would re-create loadUser on every render and cascade into
|
||||
// useEffects that depend on it.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[userQuery.refetch],
|
||||
);
|
||||
|
||||
const loadSyncStatus = useCallback(async () => {
|
||||
if (!userId) return;
|
||||
@@ -579,10 +604,9 @@ export default function AdminUserDetail() {
|
||||
useEffect(() => {
|
||||
if (!userId || isNaN(userId)) {
|
||||
navigate('/admin/users');
|
||||
return;
|
||||
}
|
||||
loadUser();
|
||||
}, [userId, loadUser, navigate]);
|
||||
// user data is auto-loaded by userQuery (enabled when userId is valid)
|
||||
}, [userId, navigate]);
|
||||
|
||||
// Load panel info when subscription changes (separate from mount to avoid redundant loadUser)
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user