Skip to main content

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

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} />
}

Performance considerations

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[]
}

Build docs developers (and LLMs) love