ChannelList
ChannelList renders the channels of one ChannelPaginator, a row per channel, and loads the next page as the user scrolls. The paginator holds the query and the loaded channels; the list only renders them. Channel list UI explains what the list renders and reads, and Channel lists covers the feature end to end.
Best practices
- Create the paginator once for the user, not on every render, and register it on
client.channelManager. An unregistered paginator shows its first page and then never changes. - Scope the paginator's filters to the user, for example with
members: { $in: [userId] }. Without that, a list can show channels the user isn't a member of. - Give each list its own paginator with its own
id. - Remove and dispose of the paginator when the user signs out.
ChannelListnever disposes of it. - Pass
onSelect, and open the channel from it. The list holds no selection and doesn't navigate.
Rendering a list
Create a paginator, register it on client.channelManager, and pass it to ChannelList:
import { useEffect, useMemo } from "react";
import { ChannelPaginator } from "stream-chat";
import type { Channel, ChannelFilters, SortParamRequest } from "stream-chat";
import { ChannelList, useChatContext } from "stream-chat-react-native";
const sort: SortParamRequest[] = [{ direction: -1, field: "last_message_at" }];
export const ChannelListScreen = ({
onSelect,
userId,
}: {
onSelect: (channel: Channel) => void;
userId: string;
}) => {
const { client } = useChatContext();
const paginator = useMemo(() => {
const filters: ChannelFilters = {
members: { $in: [userId] },
type: "messaging",
};
return new ChannelPaginator({ client, filters, id: "inbox", sort });
}, [client, userId]);
// A registered list is kept up to date as events arrive.
useEffect(() => {
client.channelManager.insertPaginator({ paginator });
return () => {
client.channelManager.removePaginator(paginator);
paginator.dispose();
};
}, [client, paginator]);
return <ChannelList onSelect={onSelect} paginator={paginator} />;
};Here the screen owns the paginator, so it's disposed of when the screen unmounts. An app that keeps the list's channels across screens creates the paginator next to the client instead. Filters, sort, registering and disposing are explained on Channel list paginators.
What it does
- Loads the first page when the paginator has none and a user is connected: on mount, when it gets another paginator, and after
client.disconnectUser()resets the paginator. A paginator that already holds channels is shown as it is. - Loads the next page when the user scrolls within
loadMoreThresholdof the end. - Refreshes the first page when the user pulls the list down, keeping the loaded channels on screen. A pull within 5 seconds of the last refresh, or of the list mounting, is ignored unless the last query failed.
- Starts the event subscriptions of
client.channelManagerwhile it's mounted. Every mounted list shares them, and they stop when the last list unmounts. - Provides
ChannelsContextto its rows, and renders aNotificationListfor the notifications aimed at the list.
It renders the rows in ChannelListView, a React Native FlatList.
UI customization
Register replacements with WithComponents. ChannelList and the parts it renders read these keys:
| Override key | Replaces | Gets |
|---|---|---|
ChannelPreview |
The content of each row. The default is ChannelPreviewView. |
channel, muted, pinned, unread, lastMessage |
ChannelListLoadingIndicator |
The placeholder rows while the first page loads | nothing |
Skeleton |
Each placeholder row | nothing |
ChannelListFooterLoadingIndicator |
The spinner after the last row while the next page loads | nothing |
EmptyStateIndicator |
"No conversations yet", when the list has no channels | listType: 'channel' |
LoadingErrorIndicator |
The error state when the first page fails. Tapping it loads the first page again. | error, listType: 'channel', loadNextPage, retry |
ChannelListHeaderErrorIndicator |
The banner over the list when a query fails while channels are on screen | onPress, which refreshes the list |
ChannelListHeaderNetworkDownIndicator |
The "Reconnecting..." banner while the network or the WebSocket is down | nothing |
ListHeaderComponent |
Nothing by default. Rendered above the first row, and scrolls with the list. | nothing |
NotificationList |
The list's notifications | nothing |
The row's parts, such as the title and the unread badge, have keys of their own; see Channel preview. ChannelList itself has no override key.
EmptyStateIndicator and LoadingErrorIndicator are shared with the message list, which passes listType: 'message', so a replacement checks listType. Loading, empty and error states explains when each one shows.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
paginator |
ChannelPaginator |
required | The paginator whose channels the list renders. Register it on client.channelManager. |
additionalFlatListProps |
Partial<FlatListProps<Channel>> |
{} |
Props spread on the FlatList after the ones the list sets, so they take precedence. Use setFlatListRef for the ref. |
channelRenderFilterFn |
(channels: Array<Channel>) => Array<Channel> |
Filters the loaded channels before they render. It runs on every render of the list, so keep it a cheap pure function. The paginator still holds every channel. | |
getChannelActionItems |
GetChannelActionItems |
the default items | Receives the default channel actions of a row and returns the ones to show. See Changing the items. |
loadMoreThreshold |
number |
0.1 |
How close to the end the user scrolls before the next page loads, in multiples of the list's visible length: the FlatList's onEndReachedThreshold. |
maxUnreadCount |
number |
255 |
The highest count the unread badge shows. A larger count shows as this number. |
mutedStatusPosition |
'trailingBottom' | 'inlineTitle' |
'inlineTitle' |
Where a muted channel's icon shows: next to the title, or at the end of the message line. |
notificationHostId |
string |
a random id the list generates once | The notification host of the list's NotificationList. See Routing notifications. |
numberOfSkeletons |
number |
8 |
The number of placeholder rows while the first page loads. |
onSelect |
(channel: Channel) => void |
Runs when the user taps a row, with its channel. While a row's swipe actions are open, a tap closes them instead. Without it, a tap does nothing else. | |
pinnedStatusPosition |
'trailingBottom' | 'inlineTitle' |
'inlineTitle' |
Where a pinned channel's icon shows: next to the title, or at the end of the message line. |
setFlatListRef |
(ref: FlatList<Channel> | null) => void |
Receives the list's FlatList ref, and null when it unmounts. |
|
swipeActionsEnabled |
boolean |
true |
Lets the user swipe a row to reveal its actions. See Swipe actions. |
The FlatList in the type of setFlatListRef is the one from react-native-gesture-handler; the list renders React Native's.
Everything else about a list is set on its paginator and on client.channelManager: filters, sort, page size, and how events move channels between lists. See Channel list paginators and Channel manager and ChannelPaginator.
Examples
Scroll to the top
import { useRef } from "react";
import { Button, View } from "react-native";
import type { Channel, ChannelPaginator } from "stream-chat";
import { ChannelList } from "stream-chat-react-native";
type ListRef = {
scrollToOffset: (params: { animated?: boolean; offset: number }) => void;
};
export const ScrollableChannelList = ({
onSelect,
paginator,
}: {
onSelect: (channel: Channel) => void;
paginator: ChannelPaginator;
}) => {
const listRef = useRef<ListRef | null>(null);
return (
<View style={{ flex: 1 }}>
<Button
onPress={() =>
listRef.current?.scrollToOffset({ animated: true, offset: 0 })
}
title="Back to top"
/>
<ChannelList
onSelect={onSelect}
paginator={paginator}
setFlatListRef={(ref) => {
listRef.current = ref;
}}
/>
</View>
);
};Several lists
Register one paginator per list, each with its own id, and render one ChannelList for each. Several lists has an example with tabs.
Change the query
Set the paginator's staticFilters or sort, and reload it, or create a paginator with another id. Changing filters or sort shows both.
Related hooks
useChannelsContext()returnsChannelsContextinside a list: the paginator, the list's configuration and its loading functions.useStateStore(paginator.state, selector)reads the loaded channels and whether more are loading. See Loading data hooks.- The hooks a row uses, such as
useChannelPreviewDisplayName, are on Channel preview hooks.