State and entity hooks
These hooks give a component the client, the channel or the thread it renders in, and a way to read any store. They're exported from stream-chat-react-native and stream-chat-expo.
How to use them, the useStateStore rules and the pitfalls are on Reading state in components. What a store is, and which objects have one, is on Reactive state.
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.
useStateStore
Subscribes to one store and returns the part of its value that a selector picks.
import type { StateStore } from "@stream-io/state-store";
type Selection = Readonly<Record<string, unknown> | Readonly<unknown[]>>;
declare function useStateStore<
T extends Record<string, unknown>,
O extends Selection,
>(store: StateStore<T>, selector: (state: T) => O): O;
declare function useStateStore<
T extends Record<string, unknown>,
O extends Selection,
>(store: StateStore<T> | undefined, selector: (state: T) => O): O | undefined;- Returns: what the selector returns, an object or a tuple.
undefinedwhenstoreisundefined, so a component can passthread?.state. - Reads: the store you pass.
StateStoreis the store type from@stream-io/state-store. - Re-renders: when a value in the selection changes. The hook compares each key of the previous selection with
===. A differentstoreorselectormakes it subscribe again, so define selectors at module scope. - Needs: nothing. It works outside
Chat.
The unread thread count has no hook of its own. Read it from client.threads.state:
import { Text } from "react-native";
import type { ThreadManagerState } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react-native";
const unreadSelector = (state: ThreadManagerState) => ({
unreadThreadCount: state.unreadThreadCount,
});
export const UnreadThreadsBadge = () => {
const { client } = useChatContext();
const { unreadThreadCount } = useStateStore(
client.threads.state,
unreadSelector,
);
if (!unreadThreadCount) return null;
return <Text>{unreadThreadCount}</Text>;
};useChatContext
Returns what Chat provides.
useChatContext(): ChatContextValue- Returns:
client: the client passed toChat.getAppSettings(): resolves to the app settings. The first call for a user requests them, and later calls reuse the result. A failed request isn't kept, so the next call requests again. With offline support, a successful request is stored in the offline database, and a failed one falls back to the stored copy.Chatmakes the first call itself once a user is connected.isMessageAIGenerated: theChatprop of the same name. It returnsfalsefor every message by default.
- Reads: React context. It subscribes to no store, so reading a getter such as
client.userfrom it doesn't re-render the component when the value changes. - Re-renders: when
Chatgets anotherclientor anotherisMessageAIGeneratedfunction.getAppSettingskeeps its identity. PassisMessageAIGeneratedfrom module scope, or every render ofChatre-renders every component that reads the context. - Needs:
Chat. Throws outside it.
The context is on ChatContext.
useCreateChatClient
Creates a client and connects a user.
useCreateChatClient(params: {
apiKey: string;
tokenOrProvider: TokenOrProvider;
userData: ClientUser;
options?: DeepPartial<InstanceConfigTree>;
}): StreamChat | nullThe params:
-
apiKey: your Stream API key. -
tokenOrProvider: the user's token, or a function that returns one. -
userData: the user to connect, asclient.connectUser()takes it. Onlyidis required. -
options: the configuration tree, the same oneclient.config.set()takes. The hook reads it on the first render only. Unless it setsclient.networkConnection.statusReporteritself, the hook addsnetInfoStatusReporter, so the network status is known from the start.Chatkeeps a reporter set here. -
Returns:
nulluntilclient.connectUser()resolves, and then the client. Each client is a newStreamChat, not the shared instance ofStreamChat.getInstance(). -
Reads: no store and no context.
-
Re-renders: when the client connects, and when it's replaced. A new
apiKey, a newtokenOrProvideror auserDatawith anotheriddisconnects the user, returnsnull, and connects a new client. Other changes touserDataare ignored. Define a token provider at module scope or withuseCallback, or every render creates a new client. -
Needs: nothing. Call it above
Chat, and pass the client toChatonce it isn'tnull.
When the component unmounts, the hook waits for the connection to finish, then disconnects the user. If client.connectUser() rejects, for example on a cold start without a network, the hook keeps returning null. To open the app on locally stored data with offline support, create the client yourself, as Offline support shows. Quick start uses the hook.
useChannel
Returns the channel the component renders in.
useChannel(): Channel- Returns: inside a
Thread, the thread's channel. Otherwise the channel of the nearestChannel. - Reads: the
ThreadandChannelcontexts. No store. - Re-renders: when the
Threador theChannelabove switches to another thread or channel, or the channel is frozen or unfrozen. Other changes to the channel's state don't re-render it: read them withuseStateStore(channel.state, selector). - Needs: a
Channelor aThread. Throws outside both.
Inside a Thread, channel.messagePaginator and channel.messageComposer are the channel's, not the thread's. The right owner inside threads says which to read instead. The channel's state is on What channels and threads hold.
useChannelContext
Returns the channel and the display settings of the nearest Channel or Thread.
useChannelContext(): ChannelContextValue- Returns:
channel: the channel. Inside aThread, the thread's channel.disabled: whether the channel is frozen.enforceUniqueReaction,enableMessageGroupingByUser,maxTimeBetweenGroupedMessages,hideDateSeparators,hideStickyDateHeaderandallowDateSeparatorForSystemMessages: the props of the same names onChannelorThread.
- Reads: the context
ChannelandThreadprovide. No store. - Re-renders: when the channel instance changes, or the channel is frozen or unfrozen. A change to one of the display props after mount doesn't provide a new value.
- Needs: a
Channelor aThread. Throws outside both.
useChannel() returns the same channel. Use useChannelContext() when you also need the settings. The context is on ChannelContext.
useThreadContext
Returns the thread the component renders in.
useThreadContext(): ThreadContextValue- Returns:
{ thread, allowThreadMessagesInChannel }.threadis theThread's thread, andallowThreadMessagesInChannelits prop of that name,trueby default. Outside aThread,{ thread: undefined, allowThreadMessagesInChannel: false }. - Reads: the context
Threadprovides. No store. - Re-renders: when the
Threadabove switches to another thread, or itsallowThreadMessagesInChannelprop changes. - Needs: nothing. Never throws, so a component can tell whether it renders in a thread.
The SDK has no hook for a thread's state. Read thread.state with useStateStore. Its fields are on What channels and threads hold and Thread and thread manager.
import { Text } from "react-native";
import type { ThreadState } from "stream-chat";
import { useStateStore, useThreadContext } from "stream-chat-react-native";
const replyCountSelector = (state: ThreadState) => ({
replyCount: state.replyCount,
});
export const ReplyCount = () => {
const { thread } = useThreadContext();
const { replyCount } = useStateStore(thread?.state, replyCountSelector) ?? {};
if (replyCount === undefined) return null;
return <Text>{replyCount} replies</Text>;
};useThreadListItemContext
Returns what the ThreadList row being rendered shows.
useThreadListItemContext(): ThreadListItemContextValue- Returns:
thread: the row's thread, andchannel, its channel.parentMessage: the thread's parent message.lastReply: the newest reply, fromthread.messagePaginator.aggregateState.ownUnreadMessageCount: the current user's unread replies.draftMessage: what the user has typed in the thread's composer, orundefinedwhen it's empty.dateString: the time of the newest reply, formatted with thetimestamp.ThreadListItemtranslation key.deletedAtDateString: the time the thread was deleted, formatted the same way, orundefined.
- Reads: the context
ThreadListItemsets for its row.ThreadListItemsubscribes tothread.state, the paginator'saggregateStateand the composer's stores, and builds the value from them. - Re-renders: whenever
ThreadListItemrenders, since it passes a new value each time. It renders when one of those stores changes. - Needs: a
ThreadListItem, whichThreadListrenders for each thread. Outside one it returns an empty object, typed as the full value, so checkthreadbefore you use it.
Use it in a component you pass to WithComponents as ThreadListItem, as Replies and threads shows.
useMutedUsers
Returns the users the current user has muted.
useMutedUsers(): UserMuteResponse[]- Returns: the user mutes, each with the muted user as
target.[]before they load. - Reads:
client.mutedUsersStore, fieldmutedUsers. The client fills it when the user connects, and replaces it onnotification.mutes_updated. - Re-renders: when the list of mutes changes.
- Needs:
Chat. Throws outside it.
useMutedChannels
Returns the channels the current user has muted.
useMutedChannels(): ChannelMute[]- Returns:
client.mutedChannels, the channel mutes. - Reads:
client.mutedChannels, a plain field rather than a store. The hook reads it again when thehealth.checkevent or thenotification.channel_mutes_updatedevent arrives. The client also replaces it onconnection.ok, which the hook doesn't listen for, so the mutes of a new connection show on the nexthealth.check. - Re-renders: when one of those events replaces the list.
- Needs:
Chat. Throws outside it.
Whether one channel is muted is also in its own state: useIsChannelMuted(channel) on Channel preview hooks reads it.
Stores without a hook
Read these stores with useStateStore and your own selector:
| To read | Store and field |
|---|---|
| Blocked user IDs | client.blockedUsers, userIds |
| The unread thread count, unseen threads | client.threads.state, unreadThreadCount and unseenThreadIds |
| A channel's settings in effect | channel.configState |
| A composer's settings in effect | messageComposer.configState |
| A thread's state | thread.state, with the thread from useThreadContext() |
| A poll's state | poll.state, with the poll from usePollContext() |
The settings stores are explained on Configuration. The connected user, client.user, has no store: read it when you need it, or listen for the events that change it.
Other exported hooks
Context hooks of components
Each component that provides a context exports a hook for it. The values are listed with the component:
| Hook | Provided by | Reference |
|---|---|---|
useOverlayContext |
OverlayProvider |
OverlayContext |
useComponentsContext |
WithComponents, and the defaults |
ComponentsContext |
useOwnCapabilitiesContext |
Channel and Thread |
OwnCapabilitiesContext |
useMessagesContext |
Channel and Thread |
MessagesContext |
useMessageContext |
Message |
MessageContext |
useMessageInputContext |
Channel and Thread |
MessageInputContext |
useChannelsContext |
ChannelList |
ChannelsContext |
useThreadsContext |
ThreadList |
ThreadList |
usePollContext |
Poll |
Poll components |
useChannelDetailsContext and the context hooks of its screens |
ChannelDetails |
Channel details contexts |
useImageGalleryContext |
OverlayProvider |
Image gallery |
useAttachmentPickerContext |
Channel and Thread |
Attachment picker |
useTheme |
OverlayProvider and Chat |
Theming |
useTranslationContext |
Chat and OverlayProvider |
Localization |
The hooks of the composer, the message, polls, notifications and channel details are on their own pages in this section.
Hooks that belong to a feature
- Audio playback:
useAudioPlayer,useActiveAudioPlayeranduseAudioPlayerContext. See Voice recordings and audio playback. - The message menu:
useHasActiveId, which returnstruewhile the menu of a message is open. See OverlayContext. - Bottom sheets:
useBottomSheetContext, which the content of aBottomSheetModalcalls toclose()the sheet with its animation, or todismiss()it before another native modal opens. See Overlays and bottom sheets. - The image gallery:
useImageGalleryVideoPlayer. See Image gallery. - Accessibility:
useAccessibilityContext,useA11yLabel,useAccessibilityActivateAction,useAccessibilityAnnouncer,useAccessibilityServiceEnabled,useAnnounceOnShow,useAnnounceOnStateChange,useIncomingMessageAnnouncements,useReducedMotionPreference,useResolvedModalAccessibilityProps,useScreenReaderEnabled,useScreenReaderMountFocusanduseSetAccessibilityFocus. See Accessibility. - The keyboard:
useKeyboardContextanduseAfterKeyboardOpenCallback. See Keyboard and insets. - Translations:
useStreami18n, whichChatcalls to start aStreami18ninstance. See Localization.
Utilities
These hooks don't read chat state. The SDK's components use them, and they're exported for components of your own:
| Hook | What it does |
|---|---|
useStableCallback(callback) |
Returns a function that keeps its identity and always calls the latest callback. |
useViewport(rounded?) |
Returns { vh, vw }, which convert a percentage of the window's height or width to points. Updates when the window resizes. |
useScreenDimensions(rounded?) |
The same as useViewport, measured against the screen instead of the window. It's deprecated, because the two differ in split view and on foldables. Use useViewport. |
useScreenOrientation() |
Returns 'landscape' while the window is wider than it is tall, 'portrait' otherwise. |
useAppStateListener(onForeground?, onBackground?) |
Calls onBackground when the app goes to the background, and onForeground when it becomes active again. The inactive state calls neither. |
useIsSvg(uri) |
Returns whether uri points to an SVG, by its .svg extension or an image/svg+xml data URI. |
useLoadingImage() |
Returns { isLoadingImage, isLoadingImageError, setLoadingImage, setLoadingImageError, onReloadImage } for an image component, and reloads a failed image when the device comes back online. Needs Chat. |
Hooks left out of this reference
The rest of the exported hooks are the SDK's own plumbing. They're exported so the default components can be rebuilt from parts, but they change with the components that use them:
- The
useCreate*Contexthooks (useCreateChatContext,useCreateChannelContext,useCreateMessagesContext,useCreateMessageContext,useCreateThreadContext,useCreateMessageInputContext,useCreateInputMessageInputContext) build the values the providers pass down. Read the contexts with theiruse*Contexthooks instead. - The message overlay and portal hooks (
useOverlayController,useIsOverlayActive,useIsOverlayClosing,useIsTopOverlayHostLayer,useShouldTeleportToClosingPortal,useClosingPortalHostBlacklist,useClosingPortalHostBlacklistState,useClosingPortalLayouts,usePortalSettledCallback,useWithPortalKeyboardSafety,useMessageOverlayRuntimeContext,useMessageOverlayTargetContext) drive the message context menu. Overlays and bottom sheets explains what you can change there. useRAFCoalescedValue,useMessageList,useMicPositionContextanduseChatConfigContextserve the message list, the voice recorder and image resizing.useInAppNotificationsStateis deprecated. It reads a store of its own, which no SDK component shows. Use the notification hooks on Notification hooks, which readclient.notifications.openInAppNotification(notification)becomesaddNotification({ message, origin, options })fromuseNotificationApi(),closeInAppNotification(id)becomesremoveNotification(id), andnotificationsbecomesuseNotifications().NotificationListcan replace a list of your own that rendered them, as Displaying notifications shows.