sequenceDiagram
box Stream Chat Client
participant Client State
participant WebSocket Client
end
box Stream React SDK
participant UI Components
end
Client State->>WebSocket Client: attach listeners to keep the state up-to-date
UI Components->>WebSocket Client: attach listeners to keep UI up-to-date
WebSocket Client->>Client State: receive a WebSocket message and trigger listeners
Client State->>Client State: update client state
WebSocket Client->>UI Components: receive a WebSocket message and trigger listeners
UI Components->>Client State: reach for updated client state
UI Components->>UI Components: update UI
SDK State Management
Most of the application state which is the main driver behind the UI changes of our chat SDK lives within our low-level client (LLC) - StreamChat
- more specifically client and channel instances and is NOT REACTIVE - our React SDK makes these state stores reactive by listening to the same events client does and copies it with the help of useState
API after it has been changed. Optimistic updates - if applicable - are handled by and are local only to React SDK.
This is how the SDK state pipeline looks like behind the scenes:
Active Channel & Channel State
The SDK comes with ChatContext
which holds (among other things) currently active channel instance and forwards LLC instance passed to the Chat
component.
Before you can access the reactive channel state you’ll need to set the channel instance as active. The channel becomes active when:
it’s user-selected in the
ChannelList
component (if you’re using our (default setup), you probably already haveChannelList
in your application)it’s passed to the
channel
prop of theChannel
componentimport { useState, useEffect } from 'react'; import { Channel, useChatContext } from 'stream-chat-react'; export const ChannelWrapper = ({ channelId, channelType = 'messaging', children }) => { const [activeChannel, setActiveChannel] = useState(undefined); const { client } = useChatContext(); useEffect(() => { if (!channelId) return; const channel = client.channel(channelType, channelId); setActiveChannel(channel); }, [channelId, channelType]); return <Channel channel={activeChannel}>{children}</Channel>; };
it’s set as active by calling the
setActiveChannel
function coming from theChatContext
(this function is used byChannelList
behind the scenes)import { useEffect } from 'react'; import { useCreateChatClient, useChatContext, Chat, Channel } from 'stream-chat-react'; const ActiveChannelSetter = ({ channelId, channelType }) => { const { client, setActiveChannel } = useChatContext(); useEffect(() => { const channel = client.channel(channelType, channelId); setActiveChannel(channel); }, [channelType, channelId]); return null; }; const App = () => { const client = useCreateChatClient(userData); if (!client) return <div>Loading...</div>; return ( <Chat client={client}> <ActiveChannelSetter channelId='random' channelType='messaging' /> <Channel>{'...other components...'}</Channel> </Chat> ); };
You can use either channel
prop on the Channel
component or setActiveChannel
function. You cannot use both at the same time.
Currently active channel state and channel instance can be accessed through the ChannelStateContext
with the help of the useChannelStateContext
hook - meaning any component which is either direct or indirect child of the Channel
component can access such state.
The example bellow shows how to reach members
and channel
property coming from the channel state:
import { useEffect } from 'react';
import { Channel, useChannelStateContext } from 'stream-chat-react';
const MembersCount = () => {
const { members, channel } = useChannelStateContext();
useEffect(() => {
console.log(`Currently active channel changed, channelId: ${channel.id}`);
}, [channel]);
return <div>{Object.keys(members).length}</div>;
};
const ChannelWrapper = () => (
<Channel>
<MembersCount />
</Channel>
);
Channel List State
ChannelList
component is a standalone component which (unsurprisingly) holds and manages list of channels. You can access loaded channels
from ChannelListContext
with the help of useChannelListContext
hook. Any component which is either direct or indirect child of the ChannelList
component can access such state (Channel Preview for example).
import { ChannelList, ChannelPreviewMessenger } from 'stream-chat-react';
import type { ChannelListProps } from 'stream-chat-react';
const CustomPreviewUI = (props) => {
const { channels } = useChannelListContext();
return <ChannelPreviewMessenger {...props} />;
};
export const CustomChannelList = (props: ChannelListProps) => {
return <ChannelList Preview={CustomPreviewUI} {...props} />;
};
Conclusion
This guide covers the biggest and most important state stores, see other React stateful contexts exported by our SDK for more information.
Mentioned in this article:
Other data/action providers: