Loading data hooks
These hooks read channel lists, message lists, the thread list and search results. The paginators behind them are explained under State → Loading data: How pagination works, Channel list paginators, Message paginators and Search sources. How to read state in components is on Reading state in components.
Each entry gives the signature, then:
- Returns: what the hook gives back.
- Reads: the store or the context it reads.
- Re-renders: what makes the component render again.
- Needs: where the component must sit, and what the hook does outside that place.
useChannelListContext
Returns the channel list paginator of the list around the component.
useChannelListContext(): ChannelListContextValue- Returns:
{ paginator }, or{}when no list provides one. - Reads: the context of
ChannelLists, whichChannelNavigationrenders while no search is active. It provides the first paginator registered onclient.channelManager. AChannelListyou render yourself doesn't provide it. - Re-renders: when the provided paginator changes. Not when its channels change: read those with
useChannelPaginatorState. - Needs: nothing. Never throws.
To provide the paginator of a list you render yourself, wrap the list in ChannelListContextProvider. The SDK reads this context to show channel list notifications in the list's panel.
import type { ChannelPaginator } from "stream-chat";
import { ChannelList, ChannelListContextProvider } from "stream-chat-react";
export const ArchivedChannels = ({
paginator,
}: {
paginator: ChannelPaginator;
}) => (
<ChannelListContextProvider value={{ paginator }}>
<ChannelList paginator={paginator} />
</ChannelListContextProvider>
);The context is on ChannelListContext.
useChannelPaginatorState
Returns the loaded channels of a channel list, and its loading state.
useChannelPaginatorState(paginator: ChannelPaginator): ChannelPaginatorView- Returns:
channels: the loaded channels, in list order.undefineduntil the first page loads.hasMoreTail: whether more channels can load at the tail, further down the list, withChannelPaginator.toTail().hasMoreHead: whether more can load at the head, withChannelPaginator.toHead().isLoading: whether a page is loading.lastQueryError: the error of the last failed query.
- Reads:
paginator.state. - Re-renders: when one of these values changes.
- Needs: no context. Pass a paginator: the hook doesn't accept
undefined, so check the one fromuseChannelListContext()before you render a component that calls it.
channels holds the channels of the loaded pages. A channel added out of order, such as one opened from search, is in it only when it sorts within those pages. Otherwise it appears when the page it belongs to loads. The paginator's fields are on Channel manager and ChannelPaginator.
import type { ChannelPaginator } from "stream-chat";
import { useChannelPaginatorState } from "stream-chat-react";
export const ChannelListFooter = ({
paginator,
}: {
paginator: ChannelPaginator;
}) => {
const { channels, hasMoreTail, isLoading, lastQueryError } =
useChannelPaginatorState(paginator);
if (lastQueryError) {
return (
<button onClick={() => paginator.reload()} type="button">
Couldn't load channels. Retry
</button>
);
}
if (!hasMoreTail) return <span>{channels?.length ?? 0} channels</span>;
return (
<button
disabled={isLoading}
onClick={() => paginator.toTail()}
type="button"
>
Load more
</button>
);
};useMessagePaginator
Returns the message list of the channel or the thread the component renders in.
useMessagePaginator(): MessagePaginator- Returns:
thread.messagePaginatorinside aThread, otherwisechannel.messagePaginatorof the channel fromuseChannel(). It's the listMessageListandVirtualizedMessageListrender. - Reads: the thread from
useThreadContext()and the channel fromuseChannel(). No store. - Re-renders: when the thread or the channel in context changes. Not when the messages change: read those from
paginator.statewithuseStateStore. - Needs: a
Channelor aThread. Throws outside both.
import type { MessagePaginatorState } from "stream-chat";
import { useMessagePaginator, useStateStore } from "stream-chat-react";
const itemsSelector = (state: MessagePaginatorState) => ({
items: state.items,
});
export const LoadedMessageCount = () => {
const paginator = useMessagePaginator();
const { items } = useStateStore(paginator.state, itemsSelector);
return <span>{items?.length ?? 0} messages loaded</span>;
};The fields of a message list are on Message paginator, and how it loads on Message paginators.
useThreadList
Keeps client.threads current while a thread list is on screen.
useThreadList(): voidWhat it does:
- When it mounts, it reloads the thread list from the first page.
- While the page is visible, it marks the list as in use (
client.threads.activate()). When the page becomes visible again, the list reloads if threads got replies in the meantime. - When the page is hidden, and when the component unmounts, it marks the list as unused (
client.threads.deactivate()).
Once the list has been in use, it also reloads after a reconnect.
- Returns: nothing.
- Reads: no store.
- Re-renders: never by itself.
- Needs:
Chat. Throws outside it.
ThreadList calls it. Call it yourself in a thread list that doesn't render ThreadList, and read the threads from client.threads.paginator.state:
import type { PaginatorState, Thread, ThreadState } from "stream-chat";
import {
useChatContext,
useStateStore,
useThreadList,
useWorkspaceNavigation,
} from "stream-chat-react";
const threadsSelector = (state: PaginatorState<Thread>) => ({
isLoading: state.isLoading,
threads: state.items,
});
const titleSelector = (state: ThreadState) => ({ title: state.title });
const ThreadRow = ({ thread }: { thread: Thread }) => {
const { openThread } = useWorkspaceNavigation();
const { title } = useStateStore(thread.state, titleSelector);
return (
<li>
<button onClick={() => openThread(thread)} type="button">
{title}
</button>
</li>
);
};
export const CompactThreadList = () => {
const { client } = useChatContext();
const { isLoading, threads } = useStateStore(
client.threads.paginator.state,
threadsSelector,
);
useThreadList();
if (isLoading && !threads?.length) return <span>Loading threads…</span>;
return (
<ul>
{threads?.map((thread) => (
<ThreadRow key={thread.id} thread={thread} />
))}
</ul>
);
};The thread list is explained on Replies and threads, and its API on Thread and thread manager.
useSearchContext
Returns the search controller and the settings of the Search around the component.
useSearchContext(): SearchContextValue- Returns:
searchController: the controller fromuseChatContext().containerRefandfilterButtonsContainerRef: the search container and its filter buttons.directMessagingChannelType,disabled,exitSearchOnInputBlur,inputPropsandplaceholder: theSearchprops of the same names.
- Reads: the context
Searchprovides. No store. - Re-renders: when
Searchprovides a new value. It does so each time it renders, for example when a search starts or ends. Not when the query or the results change: readsearchController.state(isActive,searchQuery,sources) withuseStateStore. - Needs:
Search. Throws outside it.
useSearchSourceResultsContext
Returns the search source whose results are being rendered.
useSearchSourceResultsContext(): SearchSourceResultsContextValue- Returns:
{ searchSource }. - Reads: the context
SearchSourceResultssets. Once the user types a query,SearchResultsrenders oneSearchSourceResultsfor each active source. No store. - Re-renders: when
SearchSourceResultsprovides a new value. It does so each time it renders, which follows the source'sitemsandisLoading. For any field you show, readsearchSource.state(items,isLoading,hasNext,searchQuery,lastQueryError) withuseStateStore. - Needs:
SearchSourceResults. Throws outside it.
A results header that counts the results:
import type { SearchSourceState } from "stream-chat";
import {
useSearchSourceResultsContext,
useStateStore,
} from "stream-chat-react";
const resultsSelector = (state: SearchSourceState) => ({
isLoading: state.isLoading,
items: state.items,
});
export const ResultCountHeader = () => {
const { searchSource } = useSearchSourceResultsContext();
const { isLoading, items } = useStateStore(
searchSource.state,
resultsSelector,
);
return (
<div>
{searchSource.type}: {isLoading ? "searching…" : (items?.length ?? 0)}
</div>
);
};Pass it to WithComponents as SearchSourceResultsHeader. The search components are on Search components.
Channel details search hooks
stream-chat-react/channel-detail exports one hook for each list in the channel details panel. Each builds its own search source for the channel and loads the first page when it mounts. The members and pinned messages hooks wait until the channel has members or pinned messages.
useChannelMembersSearch(): { displayedMembers: ChannelMemberResponse[]; handleSearchChange: (query: string) => void; hasMembers: boolean; membersSearchSource: ChannelMemberSearchSource; resetMembersSearch: () => void; searchInputResetKey: number }
useChannelMediaSearch(): { channelMediaSearchSource: MessageSearchSource; hasNext: boolean; hasResultsLoaded: boolean; isLoading: boolean; mediaItems: ChannelMediaItem[] }
useChannelFilesSearch(): { channelFilesSearchSource: MessageSearchSource; fileItems: ChannelFileItem[]; groupCounts: number[]; hasResultsLoaded: boolean; isLoading: boolean; sections: ChannelFileSection[] }
usePinnedMessagesSearch(params?: UsePinnedMessagesSearchParams): { displayedMessages: Array<MessageResponse | LocalMessage>; handleSearchChange: (query: string) => void; hasPinnedMessages: boolean; hasSearchResultsLoaded: boolean; pinnedMessagesSearchSource: MessageSearchSource }| Hook | Searches |
|---|---|
useChannelMembersSearch |
the channel's members. Until the first results arrive, displayedMembers holds the members the channel has loaded. |
useChannelMediaSearch |
the channel's messages with image or video attachments |
useChannelFilesSearch |
the channel's messages with file or audio attachments, grouped by month |
usePinnedMessagesSearch |
the channel's pinned messages. Pass { searchSource } to use your own source with its own filters. |
- Reads: the
stateof the source each hook builds. - Re-renders: when that source's results or loading state change.
- Needs:
ChannelDetail, or aChannelDetailProvider. Throws outside both. The media, files and pinned messages hooks also needChat.
What each one returns is on Channel Detail Views.
Lists without a hook
Read these lists with useStateStore and your own selector:
| List | Store |
|---|---|
| The thread list | client.threads.paginator.state |
| Search | searchController.state, and state on each source |