# Loading data hooks

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

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](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/how-pagination-works/), [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/), [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/) and [Search sources](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/search-sources/). How to read state in components is on [Reading state in components](https://getstream.io/chat/docs/sdk/react/v15/ui/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.

```ts
useChannelListContext(): ChannelListContextValue
```

- **Returns:** `{ paginator }`, or `{}` when no list provides one.
- **Reads:** the context of `ChannelLists`, which `ChannelNavigation` renders while no search is active. It provides the first paginator registered on `client.channelManager`. A `ChannelList` you 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.

```tsx
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](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channellistcontext/).

## useChannelPaginatorState

Returns the loaded channels of a channel list, and its loading state.

```ts
useChannelPaginatorState(paginator: ChannelPaginator): ChannelPaginatorView
```

- **Returns:**
  - `channels`: the loaded channels, in list order. `undefined` until the first page loads.
  - `hasMoreTail`: whether more channels can load at the tail, further down the list, with `ChannelPaginator.toTail()`.
  - `hasMoreHead`: whether more can load at the head, with `ChannelPaginator.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 from `useChannelListContext()` 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](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/).

```tsx
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.

```ts
useMessagePaginator(): MessagePaginator
```

- **Returns:** `thread.messagePaginator` inside a `Thread`, otherwise `channel.messagePaginator` of the channel from `useChannel()`. It's the list `MessageList` and `VirtualizedMessageList` render.
- **Reads:** the thread from `useThreadContext()` and the channel from `useChannel()`. No store.
- **Re-renders:** when the thread or the channel in context changes. Not when the messages change: read those from `paginator.state` with `useStateStore`.
- **Needs:** a `Channel` or a `Thread`. Throws outside both.

```tsx
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](https://getstream.io/chat/docs/sdk/react/v15/reference/state/message-paginator/), and how it loads on [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/).

## useThreadList

Keeps `client.threads` current while a thread list is on screen.

```ts
useThreadList(): void
```

What it does:

1. When it mounts, it reloads the thread list from the first page.
2. 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.
3. 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`:

```tsx
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](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/replies-and-threads/), and its API on [Thread and thread manager](https://getstream.io/chat/docs/sdk/react/v15/reference/state/thread-and-thread-manager/).

## useSearchContext

Returns the search controller and the settings of the `Search` around the component.

```ts
useSearchContext(): SearchContextValue
```

- **Returns:**
  - `searchController`: the controller from `useChatContext()`.
  - `containerRef` and `filterButtonsContainerRef`: the search container and its filter buttons.
  - `directMessagingChannelType`, `disabled`, `exitSearchOnInputBlur`, `inputProps` and `placeholder`: the `Search` props of the same names.
- **Reads:** the context `Search` provides. No store.
- **Re-renders:** when `Search` provides 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: read `searchController.state` (`isActive`, `searchQuery`, `sources`) with `useStateStore`.
- **Needs:** `Search`. Throws outside it.

## useSearchSourceResultsContext

Returns the search source whose results are being rendered.

```ts
useSearchSourceResultsContext(): SearchSourceResultsContextValue
```

- **Returns:** `{ searchSource }`.
- **Reads:** the context `SearchSourceResults` sets. Once the user types a query, `SearchResults` renders one `SearchSourceResults` for each active source. No store.
- **Re-renders:** when `SearchSourceResults` provides a new value. It does so each time it renders, which follows the source's `items` and `isLoading`. For any field you show, read `searchSource.state` (`items`, `isLoading`, `hasNext`, `searchQuery`, `lastQueryError`) with `useStateStore`.
- **Needs:** `SearchSourceResults`. Throws outside it.

A results header that counts the results:

```tsx
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](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/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.

```ts
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 `state` of the source each hook builds.
- **Re-renders:** when that source's results or loading state change.
- **Needs:** `ChannelDetail`, or a `ChannelDetailProvider`. Throws outside both. The media, files and pinned messages hooks also need `Chat`.

What each one returns is on [Channel Detail Views](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail-views/#search--pagination-hooks).

## 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 |

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/).