veza/apps/web/src/utils/storeSelectors.ts

211 lines
5.9 KiB
TypeScript

/**
* 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 } from '@/stores/ui';
import { useLibraryStore } from '@/stores/library';
import { useChatStore } from '@/stores/chat';
import { denormalize } from '@/utils/stateNormalization';
/**
* FE-STATE-008: Optimized selectors for AuthStore
*
* These hooks only re-render when the selected values actually change.
*/
export function useAuthUser() {
return useAuthStore(useShallow((state) => state.user));
}
export function useAuthStatus() {
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() {
return useUIStore(useShallow((state) => state.theme));
}
export function useUILanguage() {
return useUIStore(useShallow((state) => state.language));
}
export function useUISidebar() {
return useUIStore(useShallow((state) => ({
sidebarOpen: state.sidebarOpen,
setSidebarOpen: state.setSidebarOpen,
})));
}
export function useUINotifications() {
return useUIStore(useShallow((state) => ({
notifications: state.notifications,
addNotification: state.addNotification,
removeNotification: state.removeNotification,
markNotificationAsRead: state.markNotificationAsRead,
clearNotifications: state.clearNotifications,
})));
}
export function useUIActions() {
return useUIStore(useShallow((state) => ({
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() {
return useLibraryStore(useShallow((state) => denormalize(state.items)));
}
export function useLibraryFavorites() {
return useLibraryStore(useShallow((state) => denormalize(state.favorites)));
}
/**
* FE-STATE-009: Get normalized state directly (for advanced use cases)
*/
export function useLibraryItemsNormalized() {
return useLibraryStore(useShallow((state) => state.items));
}
export function useLibraryFavoritesNormalized() {
return useLibraryStore(useShallow((state) => state.favorites));
}
export function useLibraryFilters() {
return useLibraryStore(useShallow((state) => ({
filters: state.filters,
setFilters: state.setFilters,
})));
}
export function useLibraryPagination() {
return useLibraryStore(useShallow((state) => state.pagination));
}
export function useLibraryStatus() {
return useLibraryStore(useShallow((state) => ({
isLoading: state.isLoading,
error: state.error,
})));
}
export function useLibraryActions() {
return useLibraryStore(useShallow((state) => ({
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() {
return useChatStore(useShallow((state) => state.conversations));
}
export function useChatCurrentConversation() {
return useChatStore(useShallow((state) => state.currentConversation));
}
export function useChatMessages(conversationId: string) {
return useChatStore(useShallow((state) => state.messages[conversationId] || []));
}
export function useChatTypingUsers(conversationId: string) {
return useChatStore(useShallow((state) => state.typingUsers[conversationId] || []));
}
export function useChatConnection() {
return useChatStore(useShallow((state) => ({
isConnected: state.isConnected,
isLoading: state.isLoading,
error: state.error,
})));
}
export function useChatActions() {
return useChatStore(useShallow((state) => ({
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<T, U>(
store: (selector: (state: T) => U) => U,
selector: (state: T) => U,
): U {
return store(useShallow(selector));
}