# State Selectors Optimization Guide

## FE-STATE-008: Optimize state selectors to prevent unnecessary re-renders

This document explains how to use optimized selectors for Zustand stores to prevent unnecessary re-renders in React components.

## Problem

When you access a Zustand store directly, the component re-renders whenever **any** part of the store changes, even if you're only using a small portion of it:

```typescript
// ❌ BAD: Re-renders on ANY store change
function MyComponent() {
  const { user, isAuthenticated } = useAuthStore();
  // This component re-renders even if only `isLoading` changes
  return <div>{user?.username}</div>;
}
```

## Solution

Use optimized selectors that only re-render when the selected values actually change:

```typescript
// ✅ GOOD: Only re-renders when `user` changes
function MyComponent() {
  const user = useAuthUser();
  return <div>{user?.username}</div>;
}
```

## Available Selectors

### Auth Store Selectors

```typescript
import {
  useAuthUser,
  useAuthStatus,
  useAuthActions,
} from '@/utils/storeSelectors';

// Get only the user
const user = useAuthUser();

// Get authentication status
const { isAuthenticated, isLoading, error } = useAuthStatus();

// Get only actions (never causes re-renders)
const { login, logout } = useAuthActions();
```

### UI Store Selectors

```typescript
import {
  useUITheme,
  useUILanguage,
  useUISidebar,
  useUINotifications,
  useUIActions,
} from '@/utils/storeSelectors';

// Get only theme
const theme = useUITheme();

// Get sidebar state and action
const { sidebarOpen, setSidebarOpen } = useUISidebar();

// Get notifications
const { notifications, addNotification } = useUINotifications();
```

### Library Store Selectors

```typescript
import {
  useLibraryItems,
  useLibraryFavorites,
  useLibraryFilters,
  useLibraryPagination,
  useLibraryStatus,
  useLibraryActions,
} from '@/utils/storeSelectors';

// Get only items
const items = useLibraryItems();

// Get favorites
const favorites = useLibraryFavorites();

// Get filters and setter
const { filters, setFilters } = useLibraryFilters();

// Get pagination info
const pagination = useLibraryPagination();
```

### Chat Store Selectors

```typescript
import {
  useChatConversations,
  useChatCurrentConversation,
  useChatMessages,
  useChatTypingUsers,
  useChatConnection,
  useChatActions,
} from '@/utils/storeSelectors';

// Get conversations
const conversations = useChatConversations();

// Get messages for a specific conversation
const messages = useChatMessages(conversationId);

// Get typing users for a conversation
const typingUsers = useChatTypingUsers(conversationId);

// Get connection status
const { isConnected, isLoading, error } = useChatConnection();
```

## Custom Selectors

If you need to select multiple values that aren't covered by the provided selectors, use `useStoreSelector`:

```typescript
import { useStoreSelector } from '@/utils/storeSelectors';
import { useAuthStore } from '@/features/auth/store/authStore';

function MyComponent() {
  // Select multiple values with shallow comparison
  const { user, isAuthenticated, isLoading } = useStoreSelector(
    useAuthStore,
    (state) => ({
      user: state.user,
      isAuthenticated: state.isAuthenticated,
      isLoading: state.isLoading,
    }),
  );

  return <div>{user?.username}</div>;
}
```

## Best Practices

### 1. Use Specific Selectors

Prefer specific selectors over accessing the entire store:

```typescript
// ❌ BAD
const store = useAuthStore();

// ✅ GOOD
const user = useAuthUser();
const { isAuthenticated } = useAuthStatus();
```

### 2. Separate Data from Actions

Actions rarely change, so separate them to avoid re-renders:

```typescript
// ❌ BAD: Re-renders when any state changes
const { items, fetchItems } = useLibraryStore();

// ✅ GOOD: Only re-renders when items change
const items = useLibraryItems();
const { fetchItems } = useLibraryActions();
```

### 3. Use Shallow Comparison for Objects

When selecting objects, use `useShallow` or the provided selectors:

```typescript
// ❌ BAD: New object reference on every render
const { user, isAuthenticated } = useAuthStore((state) => ({
  user: state.user,
  isAuthenticated: state.isAuthenticated,
}));

// ✅ GOOD: Shallow comparison prevents unnecessary re-renders
const { user, isAuthenticated } = useStoreSelector(useAuthStore, (state) => ({
  user: state.user,
  isAuthenticated: state.isAuthenticated,
}));
```

### 4. Memoize Derived Values

If you need to compute values from store data, use `useMemo`:

```typescript
import { useMemo } from 'react';
import { useLibraryItems } from '@/utils/storeSelectors';

function MyComponent() {
  const items = useLibraryItems();

  // Memoize expensive computation
  const favoriteCount = useMemo(
    () => items.filter((item) => item.is_favorite).length,
    [items],
  );

  return <div>Favorites: {favoriteCount}</div>;
}
```

## Performance Benefits

Using optimized selectors provides several benefits:

1. **Fewer Re-renders**: Components only re-render when their selected values change
2. **Better Performance**: Reduces unnecessary React reconciliation
3. **Predictable Updates**: Easier to reason about when components update
4. **Better DevTools**: Clearer view of what triggers re-renders

## Migration Guide

### Before (Unoptimized)

```typescript
function UserProfile() {
  const { user, isAuthenticated, isLoading, error } = useAuthStore();
  const { theme, language } = useUIStore();

  // Re-renders on ANY change to auth or UI stores
  return <div>{user?.username}</div>;
}
```

### After (Optimized)

```typescript
import {
  useAuthUser,
  useUITheme,
  useUILanguage,
} from '@/utils/storeSelectors';

function UserProfile() {
  const user = useAuthUser();
  const theme = useUITheme();
  const language = useUILanguage();

  // Only re-renders when user, theme, or language actually change
  return <div>{user?.username}</div>;
}
```

## Testing

When testing components that use selectors, you can still use the store directly:

```typescript
import { render } from '@testing-library/react';
import { useAuthStore } from '@/features/auth/store/authStore';

test('renders user profile', () => {
  // Set up store state
  useAuthStore.setState({
    user: { id: '1', username: 'testuser' },
    isAuthenticated: true,
  });

  const { getByText } = render(<UserProfile />);
  expect(getByText('testuser')).toBeInTheDocument();
});
```

## Related Documentation

- [Zustand Selectors](https://github.com/pmndrs/zustand#selecting-multiple-state-slices)
- [Zustand Shallow Comparison](https://github.com/pmndrs/zustand#shallow-equality-check)
- [React Performance Optimization](https://react.dev/learn/render-and-commit)
