Overview
The chatSelectors object provides reusable selector functions for efficiently accessing derived state from useChatStore. These selectors transform the normalized state structure into convenient formats.
import { useChatStore, chatSelectors } from "@/lib/chat/state"
function Component() {
const chats = useChatStore(chatSelectors.chatList)
const activeChat = useChatStore(chatSelectors.activeChat)
// ...
}
Using these selectors with Zustand optimizes re-renders by subscribing only to the specific derived values your component needs.
Selectors
chatList
chatList: (state: ChatStore) => Chat[]
Converts the normalized chat dictionary to an array of chat objects.
Returns: Array of all Chat objects
Example:
const allChats = useChatStore(chatSelectors.chatList)
// Returns: [{ id: "1", contactId: "contact-1", ... }, ...]
Use case: Rendering chat list in sidebar, filtering/sorting chats
activeChat
activeChat: (state: ChatStore) => Chat | undefined
Retrieves the currently active chat based on activeChatId.
Returns: Active Chat object, or undefined if no chat is active
Example:
const currentChat = useChatStore(chatSelectors.activeChat)
if (currentChat) {
// Render active chat UI
}
Use case: Displaying active conversation, conditional rendering
activeMessages
activeMessages: (state: ChatStore) => Message[]
Retrieves all messages for the currently active chat in chronological order.
Returns: Array of Message objects for active chat, or empty array if no active chat
Example:
const messages = useChatStore(chatSelectors.activeMessages)
// Returns: [{ id: "msg-1", text: "Hello", ... }, { id: "msg-2", ... }]
Implementation details:
- Returns empty array
[] if no chat is active
- Maps
chat.messageIds to actual message objects from state.messages
- Filters out any missing messages (defensive programming)
- Maintains message order from the chat’s
messageIds array
Use case: Rendering message thread, scrolling to latest message
contacts: (state: ChatStore) => Contact[]
Converts the normalized contacts dictionary to an array of contact objects.
Returns: Array of all Contact objects
Example:
const allContacts = useChatStore(chatSelectors.contacts)
// Returns: [{ id: "1", name: "Alice", phoneNumber: "+1234...", ... }, ...]
Use case: Contact list display, search/filter contacts, new chat creation
Usage patterns
Basic selector usage
function ChatList() {
const chats = useChatStore(chatSelectors.chatList)
return (
<div>
{chats.map(chat => <ChatItem key={chat.id} chat={chat} />)}
</div>
)
}
Combining selectors
function ActiveChatHeader() {
const chat = useChatStore(chatSelectors.activeChat)
const contacts = useChatStore((state) => state.contacts)
if (!chat) return null
const contact = contacts[chat.contactId]
return <Header name={contact.name} status={contact.isOnline} />
}
Custom selectors
You can create custom selectors following the same pattern:
const unreadChats = (state: ChatStore) =>
Object.values(state.chats).filter(chat => chat.unreadCount > 0)
function Component() {
const unread = useChatStore(unreadChats)
return <Badge count={unread.length} />
}
Selectors enable fine-grained subscriptions. Components only re-render when their selected values change, not when unrelated store state updates.
Optimization tips:
- Use selectors instead of accessing the entire store
- Create custom selectors for complex derived state
- Avoid inline arrow functions in selectors for referential equality
Type definitions
export const chatSelectors = {
chatList: (state: ChatStore) => Chat[]
activeChat: (state: ChatStore) => Chat | undefined
activeMessages: (state: ChatStore) => Message[]
contacts: (state: ChatStore) => Contact[]
}