mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
perf: add Zustand selectors to prevent cascading re-renders
Replace all bare useAuthStore(), useBlockingStore(), and useSuccessNotification() calls with individual field selectors. This prevents components from re-rendering when unrelated store fields change. - 21 useAuthStore usages across 20 files: individual selectors for 1-2 fields, useShallow for 3+ fields - 5 useBlockingStore usages: individual selectors - 2 useSuccessNotification usages: individual selectors
This commit is contained in:
@@ -40,7 +40,7 @@ const WalletIcon = ({ className = 'h-8 w-8' }: { className?: string }) => (
|
||||
|
||||
export default function Balance() {
|
||||
const { t } = useTranslation();
|
||||
const { refreshUser } = useAuthStore();
|
||||
const refreshUser = useAuthStore((state) => state.refreshUser);
|
||||
const queryClient = useQueryClient();
|
||||
const { formatAmount, currencySymbol } = useCurrency();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
@@ -14,7 +14,8 @@ import InstallationGuide from '../components/connection/InstallationGuide';
|
||||
export default function Connection() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { user, isAdmin } = useAuthStore();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const isAdmin = useAuthStore((state) => state.isAdmin);
|
||||
const { isTelegramWebApp } = useTelegramSDK();
|
||||
const { impact: hapticImpact } = useHaptic();
|
||||
|
||||
|
||||
@@ -48,14 +48,15 @@ const RefreshIcon = ({ className = 'w-4 h-4' }: { className?: string }) => (
|
||||
|
||||
export default function Dashboard() {
|
||||
const { t } = useTranslation();
|
||||
const { user, refreshUser } = useAuthStore();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const refreshUser = useAuthStore((state) => state.refreshUser);
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const { formatAmount, currencySymbol, formatPositive } = useCurrency();
|
||||
const [trialError, setTrialError] = useState<string | null>(null);
|
||||
const { isCompleted: isOnboardingCompleted, complete: completeOnboarding } = useOnboarding();
|
||||
const [showOnboarding, setShowOnboarding] = useState(false);
|
||||
const { blockingType } = useBlockingStore();
|
||||
const blockingType = useBlockingStore((state) => state.blockingType);
|
||||
|
||||
// Refresh user data on mount
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useLocation } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { authApi } from '../api/auth';
|
||||
import { isValidEmail } from '../utils/validation';
|
||||
import {
|
||||
@@ -32,7 +33,15 @@ export default function Login() {
|
||||
loginWithTelegram,
|
||||
loginWithEmail,
|
||||
registerWithEmail,
|
||||
} = useAuthStore();
|
||||
} = useAuthStore(
|
||||
useShallow((state) => ({
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isLoading: state.isLoading,
|
||||
loginWithTelegram: state.loginWithTelegram,
|
||||
loginWithEmail: state.loginWithEmail,
|
||||
registerWithEmail: state.registerWithEmail,
|
||||
})),
|
||||
);
|
||||
|
||||
// Get referral code from localStorage (captured from ?ref= param at module level in auth store)
|
||||
const referralCode = getPendingReferralCode() || '';
|
||||
|
||||
@@ -26,7 +26,8 @@ export default function OAuthCallback() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [error, setError] = useState('');
|
||||
const { loginWithOAuth, isAuthenticated } = useAuthStore();
|
||||
const loginWithOAuth = useAuthStore((state) => state.loginWithOAuth);
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
|
||||
@@ -61,7 +61,8 @@ const PencilIcon = () => (
|
||||
|
||||
export default function Profile() {
|
||||
const { t } = useTranslation();
|
||||
const { user, setUser } = useAuthStore();
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const setUser = useAuthStore((state) => state.setUser);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
@@ -8,7 +8,8 @@ export default function TelegramCallback() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [error, setError] = useState('');
|
||||
const { loginWithTelegramWidget, isAuthenticated } = useAuthStore();
|
||||
const loginWithTelegramWidget = useAuthStore((state) => state.loginWithTelegramWidget);
|
||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { brandingApi } from '../api/branding';
|
||||
import { isInTelegramWebApp, getTelegramInitData } from '../hooks/useTelegramSDK';
|
||||
|
||||
@@ -33,7 +34,17 @@ export default function TelegramRedirect() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { loginWithTelegram, isAuthenticated, isLoading: authLoading } = useAuthStore();
|
||||
const {
|
||||
loginWithTelegram,
|
||||
isAuthenticated,
|
||||
isLoading: authLoading,
|
||||
} = useAuthStore(
|
||||
useShallow((state) => ({
|
||||
loginWithTelegram: state.loginWithTelegram,
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isLoading: state.isLoading,
|
||||
})),
|
||||
);
|
||||
const [status, setStatus] = useState<'loading' | 'success' | 'error' | 'not-telegram'>('loading');
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [retryCount, setRetryCount] = useState(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useSearchParams, Link, useNavigate } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authApi } from '../api/auth';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
import { useShallow } from 'zustand/shallow';
|
||||
import { consumeCampaignSlug } from '../utils/campaign';
|
||||
import { tokenStorage } from '../utils/token';
|
||||
import LanguageSwitcher from '../components/LanguageSwitcher';
|
||||
@@ -13,7 +14,13 @@ export default function VerifyEmail() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
|
||||
const [error, setError] = useState('');
|
||||
const { setTokens, setUser, checkAdminStatus } = useAuthStore();
|
||||
const { setTokens, setUser, checkAdminStatus } = useAuthStore(
|
||||
useShallow((state) => ({
|
||||
setTokens: state.setTokens,
|
||||
setUser: state.setUser,
|
||||
checkAdminStatus: state.checkAdminStatus,
|
||||
})),
|
||||
);
|
||||
const hasVerified = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user