Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

Loading data hooks

These hooks read channel lists, message lists, the thread list and the reminder list. The paginators behind them are explained under State > Loading data: How pagination works, Channel list paginators and Message paginators. 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.

useChannelsContext

Returns the paginator and the settings of the ChannelList around the component.

useChannelsContext(): ChannelsContextValue
  • Returns:
    • paginator: the ChannelPaginator passed to ChannelList.
    • loadNextPage(): loads the next page, with ChannelPaginator.toTail(). It does nothing until the first page has loaded.
    • reloadList(): clears the list and loads the first page again. The list shows its loading state meanwhile.
    • refreshList(): loads the first page again and keeps the channels on screen meanwhile, as pull to refresh does. A call within 5 seconds of the previous one, or of the list mounting, is ignored, unless the last query failed.
    • onSelect, getChannelActionItems, swipeActionsEnabled, additionalFlatListProps, setFlatListRef, loadMoreThreshold, maxUnreadCount, numberOfSkeletons, mutedStatusPosition and pinnedStatusPosition: the ChannelList props of the same names, with their defaults applied.
  • Reads: the context ChannelList provides. No store.
  • Re-renders: when ChannelList provides a new value, which happens when one of its props changes. Not when the channels change: read those from paginator.state with useStateStore.
  • Needs: ChannelList. Throws outside it.

The props are on ChannelsContext.

The channels of a list

No hook returns the channels. Read them from the paginator's state, which holds items (undefined until the first page loads), isLoading, hasMoreTail, hasMoreHead and lastQueryError. A footer for your own ChannelList component reads it like this:

import { Pressable, Text } from "react-native";
import type { ChannelPaginatorState } from "stream-chat";
import { useChannelsContext, useStateStore } from "stream-chat-react-native";

const footerSelector = (state: ChannelPaginatorState) => ({
  hasMoreTail: state.hasMoreTail,
  isLoading: state.isLoading,
  items: state.items,
  lastQueryError: state.lastQueryError,
});

export const ChannelListFooter = () => {
  const { loadNextPage, paginator, reloadList } = useChannelsContext();
  const { hasMoreTail, isLoading, items, lastQueryError } = useStateStore(
    paginator.state,
    footerSelector,
  );

  if (lastQueryError) {
    return (
      <Pressable accessibilityRole="button" onPress={() => reloadList()}>
        <Text>Couldn't load channels. Retry</Text>
      </Pressable>
    );
  }
  if (!hasMoreTail) return <Text>{items?.length ?? 0} channels</Text>;

  return (
    <Pressable
      accessibilityRole="button"
      disabled={isLoading}
      onPress={() => loadNextPage()}
    >
      <Text>Load more</Text>
    </Pressable>
  );
};

Outside a ChannelList, read the same store on the paginator you created. The paginator's fields are on Channel manager and ChannelPaginator.

useMessagePaginator

Returns the message list of the channel or the thread the component renders in.

useMessagePaginator(): MessagePaginator
  • Returns: thread.messagePaginator inside a Thread, otherwise channel.messagePaginator of the Channel. It's the list MessageList and MessageFlashList render.
  • Reads: the Thread and Channel contexts. 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.
import { Text } from "react-native";
import type { MessagePaginatorState } from "stream-chat";
import { useMessagePaginator, useStateStore } from "stream-chat-react-native";

const itemsSelector = (state: MessagePaginatorState) => ({
  items: state.items,
});

export const LoadedMessageCount = () => {
  const paginator = useMessagePaginator();
  const { items } = useStateStore(paginator.state, itemsSelector);

  return <Text>{items?.length ?? 0} messages loaded</Text>;
};

A message paginator's state publishes at most once every 500 ms by default, as Batched message-list updates explains. Its other stores, unreadStateSnapshot, liveViewState, aggregateState and messageFocusSignal, publish at once. The fields of a message list are on Message paginator, and how it loads on Message paginators.

useIsTargetedMessage

Says whether a message is the one the list jumped to.

useIsTargetedMessage(messageId: string): boolean
  • Returns: true while the message is the target of the paginator's focus signal. MessagePaginator.jumpToMessage() and MessagePaginator.jumpToTheFirstUnreadMessage() set the signal, and it clears itself after a short time.
  • Reads: messageFocusSignal of the paginator from useMessagePaginator(), its signal.messageId.
  • Re-renders: when the message becomes the target, and when it stops being it. A jump re-renders the row that loses the highlight and the row that gains it, not every row.
  • Needs: a Channel or a Thread. Throws outside both.

The default message highlights itself with it. Jumping to a message is explained on Moving between channels, threads and messages.

useThreadsContext

Returns the threads and the settings of the ThreadList around the component.

useThreadsContext(): ThreadsContextValue
  • Returns:
    • threads: the loaded threads, from client.threads.paginator.state.
    • isLoading: true while a page loads and no thread is loaded yet. isLoadingNext: true while a page loads and threads are already shown.
    • loadMore(): loads the next page, with client.threads.paginator.toTail().
    • isFocused, onThreadSelect and additionalFlatListProps: the ThreadList props of the same names. isFocused is undefined when the prop isn't passed, although ThreadList treats a missing prop as true.
  • Reads: the context ThreadList provides. ThreadList subscribes to client.threads.paginator.state and passes the result down.
  • Re-renders: whenever ThreadList renders, since it passes a new value each time. That includes every change to the thread list's loading state and items.
  • Needs: ThreadList. Throws outside it.

ThreadList also calls client.threads.activate() while isFocused is true, and client.threads.deactivate() when it becomes false and when the list unmounts. Activating the thread manager loads the first page when the list has none, and reloads it when threads got replies while it was inactive. ThreadListProps types isFocused as required, although it defaults to true. Pass the focus state of the screen, as Thread UI shows.

A thread list of your own, without ThreadList, reads client.threads.paginator.state with useStateStore and activates client.threads itself. Chat starts the thread manager's event handling, so render it inside Chat:

import { useEffect } from "react";
import { FlatList, Pressable, Text } from "react-native";
import type { PaginatorState, Thread, ThreadState } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react-native";

const threadsSelector = (state: PaginatorState<Thread>) => ({
  isLoading: state.isLoading,
  threads: state.items,
});

const titleSelector = (state: ThreadState) => ({ title: state.title });

const ThreadRow = ({
  onPress,
  thread,
}: {
  onPress: (thread: Thread) => void;
  thread: Thread;
}) => {
  const { title } = useStateStore(thread.state, titleSelector);

  return (
    <Pressable accessibilityRole="button" onPress={() => onPress(thread)}>
      <Text>{title}</Text>
    </Pressable>
  );
};

export const CompactThreadList = ({
  onThreadPress,
}: {
  onThreadPress: (thread: Thread) => void;
}) => {
  const { client } = useChatContext();
  const { isLoading, threads } = useStateStore(
    client.threads.paginator.state,
    threadsSelector,
  );

  useEffect(() => {
    client.threads.activate();
    return () => client.threads.deactivate();
  }, [client]);

  if (isLoading && !threads?.length) return <Text>Loading threads...</Text>;

  return (
    <FlatList
      data={threads ?? []}
      keyExtractor={(thread) => thread.id}
      onEndReached={() => client.threads.paginator.toTail()}
      renderItem={({ item }) => (
        <ThreadRow onPress={onThreadPress} thread={item} />
      )}
    />
  );
};

The thread list is explained on Replies and threads, and its API on Thread and thread manager.

useQueryReminders

Returns the reminder list, kept in step with reminder events.

useQueryReminders(): {
  data: ReminderResponseData[];
  isLoading: boolean;
  loadNext: () => Promise<void>;
  setData: Dispatch<SetStateAction<ReminderResponseData[]>>;
}

It reads client.reminders.paginator.state, and doesn't load a page by itself. The full entry is on Reminder hooks.

Lists without a hook

Read these lists with useStateStore and your own selector:

List Store
A channel list paginator.state of the paginator you pass to ChannelList
The thread list client.threads.paginator.state
A thread's replies thread.messagePaginator.state
The channel details lists searchSource.state of each list's context. See Channel details hooks.