/** * 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 { useQueryClient } from '@tanstack/react-query'; 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 { useLibraryItems as useLibraryItemsQuery, useLibraryFavorites as useLibraryFavoritesQuery, libraryQueryKeys, type LibraryItemsParams } from '@/features/library/hooks/useLibraryItems'; 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 * * NOTE: These selectors now use React Query hooks instead of Zustand store. * They maintain the same interface for backward compatibility. */ export function useLibraryItems(params: LibraryItemsParams = {}): LibraryItem[] { const { data } = useLibraryItemsQuery(params); return data?.items ?? []; } export function useLibraryFavorites(): LibraryItem[] { const { data } = useLibraryFavoritesQuery(); return data ?? []; } /** * 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(params: LibraryItemsParams = {}) { const { data } = useLibraryItemsQuery(params); return { page: data?.page ?? 1, limit: data?.limit ?? 20, total: data?.total ?? 0, has_next: data?.has_next ?? false, has_prev: data?.has_prev ?? false, }; } export function useLibraryStatus(): { isLoading: boolean; error: ApiError | null } { const itemsQuery = useLibraryItemsQuery({}); const favoritesQuery = useLibraryFavoritesQuery(); return { isLoading: itemsQuery.isLoading || favoritesQuery.isLoading, error: (itemsQuery.error || favoritesQuery.error) as ApiError | null, }; } export function useLibraryActions() { const queryClient = useQueryClient(); const libraryStore = useLibraryStore(); return { fetchItems: async (params?: LibraryItemsParams) => { await queryClient.refetchQueries({ queryKey: libraryQueryKeys.items(params), }); }, fetchFavorites: async () => { await queryClient.refetchQueries({ queryKey: libraryQueryKeys.favorites(), }); }, uploadFile: libraryStore.uploadFile, toggleFavorite: libraryStore.toggleFavorite, deleteItem: libraryStore.deleteItem, clearItems: libraryStore.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)); }