/** * Store Selectors * FE-STATE-008: Optimize state selectors to prevent unnecessary re-renders * * Provides optimized selectors for Zustand stores to prevent unnecessary re-renders. * Use these selectors instead of accessing the entire store. */ import { useShallow } from 'zustand/react/shallow'; import { useAuthStore } from '@/features/auth/store/authStore'; import { useUIStore, type UIStore } from '@/stores/ui'; import { useLibraryStore, type LibraryStore } from '@/stores/library'; import { useChatStore, type ChatStore } from '@/stores/chat'; import { denormalize } from '@/utils/stateNormalization'; import type { User, LibraryItem, ChatMessage, Conversation } from '@/types'; import type { ApiError } from '@/types/api'; /** * FE-STATE-008: Optimized selectors for AuthStore * * These hooks only re-render when the selected values actually change. */ export function useAuthUser(): User | null { return useAuthStore(useShallow((state) => state.user)); } export function useAuthStatus(): { isAuthenticated: boolean; isLoading: boolean; error: ApiError | null } { return useAuthStore(useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading, error: state.error, }))); } export function useAuthActions() { return useAuthStore(useShallow((state) => ({ login: state.login, register: state.register, logout: state.logout, refreshUser: state.refreshUser, checkAuthStatus: state.checkAuthStatus, clearError: state.clearError, setLoading: state.setLoading, }))); } /** * FE-STATE-008: Optimized selectors for UIStore */ export function useUITheme(): 'light' | 'dark' | 'system' { return useUIStore(useShallow((state: UIStore) => state.theme)); } export function useUILanguage(): 'en' | 'fr' { return useUIStore(useShallow((state: UIStore) => state.language)); } export function useUISidebar() { return useUIStore(useShallow((state: UIStore) => ({ sidebarOpen: state.sidebarOpen, setSidebarOpen: state.setSidebarOpen, }))); } export function useUINotifications() { return useUIStore(useShallow((state: UIStore) => ({ notifications: state.notifications, addNotification: state.addNotification, removeNotification: state.removeNotification, markNotificationAsRead: state.markNotificationAsRead, clearNotifications: state.clearNotifications, }))); } export function useUIActions() { return useUIStore(useShallow((state: UIStore) => ({ setTheme: state.setTheme, setLanguage: state.setLanguage, setSidebarOpen: state.setSidebarOpen, }))); } /** * FE-STATE-008: Optimized selectors for LibraryStore * FE-STATE-009: Convert normalized state to arrays for compatibility */ export function useLibraryItems(): LibraryItem[] { return useLibraryStore(useShallow((state: LibraryStore) => denormalize(state.items))); } export function useLibraryFavorites(): LibraryItem[] { return useLibraryStore(useShallow((state: LibraryStore) => denormalize(state.favorites))); } /** * FE-STATE-009: Get normalized state directly (for advanced use cases) */ export function useLibraryItemsNormalized() { return useLibraryStore(useShallow((state: LibraryStore) => state.items)); } export function useLibraryFavoritesNormalized() { return useLibraryStore(useShallow((state: LibraryStore) => state.favorites)); } export function useLibraryFilters() { return useLibraryStore(useShallow((state: LibraryStore) => ({ filters: state.filters, setFilters: state.setFilters, }))); } export function useLibraryPagination() { return useLibraryStore(useShallow((state: LibraryStore) => state.pagination)); } export function useLibraryStatus(): { isLoading: boolean; error: ApiError | null } { return useLibraryStore(useShallow((state: LibraryStore) => ({ isLoading: state.isLoading, error: state.error, }))); } export function useLibraryActions() { return useLibraryStore(useShallow((state: LibraryStore) => ({ fetchItems: state.fetchItems, fetchFavorites: state.fetchFavorites, uploadFile: state.uploadFile, toggleFavorite: state.toggleFavorite, deleteItem: state.deleteItem, clearItems: state.clearItems, }))); } /** * FE-STATE-008: Optimized selectors for ChatStore */ export function useChatConversations(): Conversation[] { return useChatStore(useShallow((state: ChatStore) => state.conversations)); } export function useChatCurrentConversation(): Conversation | null { return useChatStore(useShallow((state: ChatStore) => state.currentConversation)); } export function useChatMessages(conversationId: string): ChatMessage[] { return useChatStore(useShallow((state: ChatStore) => state.messages[conversationId] || [])); } export function useChatTypingUsers(conversationId: string): string[] { return useChatStore(useShallow((state: ChatStore) => state.typingUsers[conversationId] || [])); } export function useChatConnection(): { isConnected: boolean; isLoading: boolean; error: string | null } { return useChatStore(useShallow((state: ChatStore) => ({ isConnected: state.isConnected, isLoading: state.isLoading, error: state.error, }))); } export function useChatActions() { return useChatStore(useShallow((state: ChatStore) => ({ setConversations: state.setConversations, setCurrentConversation: state.setCurrentConversation, addMessage: state.addMessage, updateMessage: state.updateMessage, removeMessage: state.removeMessage, setMessages: state.setMessages, setTypingUsers: state.setTypingUsers, addTypingUser: state.addTypingUser, removeTypingUser: state.removeTypingUser, setConnected: state.setConnected, setLoading: state.setLoading, setError: state.setError, connect: state.connect, disconnect: state.disconnect, joinConversation: state.joinConversation, leaveConversation: state.leaveConversation, sendMessage: state.sendMessage, startTyping: state.startTyping, stopTyping: state.stopTyping, addReaction: state.addReaction, removeReaction: state.removeReaction, fetchConversations: state.fetchConversations, createConversation: state.createConversation, }))); } /** * FE-STATE-008: Helper function to create custom selectors * * Use this when you need to select multiple values from a store. * * @example * ```typescript * // Instead of: * const { user, isAuthenticated } = useAuthStore(); * * // Use: * const { user, isAuthenticated } = useStoreSelector(useAuthStore, (state) => ({ * user: state.user, * isAuthenticated: state.isAuthenticated, * })); * ``` */ export function useStoreSelector( store: (selector: (state: T) => U) => U, selector: (state: T) => U, ): U { return store(useShallow(selector)); }