<Channel MessageStatus={() => null}>
Contexts
Stream Chat for React Native uses a number of contexts to distribute data, functions, and components throughout the SDK. You can make use of these contexts when creating custom components to construct reusable components that can replace those provided out of the box.
Providers
The majority of the contexts within the SDK are established in the higher level components.
OverlayProvider
, Chat
, and Channel
all contain a number of context providers that can be accessed via hooks or Higher Order Components.
Channel
Chat
OverlayProvider
These contexts are the source of the majority of the components, functions, and data used in the SDK. When creating a custom component this is where one should first look to draw data and sub-components from to create a custom component. Most components when rendered by the SDK receive few or no props, it is up to you as the developer to pull the appropriate data from these contexts that you need for your customizations.
There are two other contexts available in the SDK, the ChannelsContext
and the MessageContext
.
The ChannelsContext
is provided by the ChannelList
component.
The MessageContext
is provided within the Message
component and wraps every MessageSimple
component rendered with a context with information unique to that message.
This allows you as a developer to create customizations to a sub-component of Message
by accessing relevant data from a given message.
Hooks
To access information from these contexts we suggest using the hooks that are provided by the library.
Context | Hook |
---|---|
AttachmentPickerContext | useAttachmentPickerContext |
ChannelContext | useChannelContext |
ChannelsContext | useChannelsContext |
ChatContext | useChatContext |
ImageGalleryContext | useImageGalleryContext |
KeyboardContext | useKeyboardContext |
MessageInputContext | useMessageInputContext |
MessageOverlayContext | useMessageOverlayContext |
MessagesContext | useMessagesContext |
OverlayContext | useOverlayContext |
PaginatedMessageListContext | usePaginatedMessageListContext |
SuggestionsContext | useSuggestionsContext |
ThreadContext | useThreadContext |
ThemeContext | useTheme |
TranslationContext | useTranslationContext |
TypingContext | useTypingContext |
If you are using TypeScript you will need to pass your custom data types to hooks you are taking advantage of.
If needed Higher Order Components are also exported to pass contexts into class based components.
Utilization
Most customizations to the UI are provided through the Channel
component.
To give some insight into how customizations work internally with contexts we will walk through the path a single custom component takes when it is provided as a prop to the Channel
component.
We will also look at how this structure can be utilized to build custom components that consume context.
For this walk-through we will follow the path of the MessageStatus
component that provides the sending and read status of a single message.
We will consider to start that we want to remove this component completely, and thus we provide a function that returns null
to the prop MessageStatus
on Channel
.
Internal to the Channel
component our MessageStatus
prop will override the default value for the prop, MessageStatusDefault
, which is our out of the box MessageStatus
component.
const { ...MessageStatus } = props;
The MessageStatus
prop is then passed into an internal hook called useCreateMessagesContext
.
const messagesContext = useCreateMessagesContext({
...MessageStatus,
});
The useCreateMessagesContext
hook utilizes the useMemo
hook to return a memoized version of the value that will be provided to the MessagesContext
.
This value only will update under certain conditions to reduce unnecessary updating of the context, and therefore re-rendering of components using the context.
For this reason changing a component provided to Channel
on the fly will often not result in an immediate UI update.
<MessagesProvider value={messagesContext}>
The custom MessageStatus
component can now be accessed using the useMessagesContext
hook.
The default component for MessageFooter
does just this, and then renders the MessageStatus
component if desired.
const { showMessageStatus } = useMessageContext();
const { MessageStatus } = useMessagesContext();
...
return (
...
{showMessageStatus && <MessageStatus />}
);
In this scenario <MessageStatus />
would always be null.
But if you actually wanted to replace this component with a custom component you would likely want access to the same information the default component has, or perhaps more.
To access this information we would once again reach for context, the default MessageStatus
component utilizes the useMessageContext
.
const { message, readEventsEnabled, threadList } = useMessageContext();
A custom component can do the same.
Because custom components will ultimately be rendered within the providers they can access contexts.
A simple custom MessageStatus
in this case could then just pull message
from context and return the readBy
key as text.
const CustomMessageStatus = () => {
const { message } = useMessageContext();
return <Text>{message.readBy}</Text>;
};
...
<Channel MessageStatus={CustomMessageStatus}>
Additionally custom components can access other components, custom or otherwise, from the same context they are stored in.
MessageFooter
renders MessageStatus
in the default component, but MessageFooter
itself is also stored in the MessagesContext
along with MessageStatus
, and is replaceable as well.
It is therefore possible to utilize a mix of other custom and out of the box components within a custom component to create your desired UI.
const CustomMessageFooter = () => {
const { MessageAvatar, MessageStatus } = useMessagesContext();
return (
<View>
<MessageAvatar />
<Text>Footer</Text>
<MessageStatus />
</View>
);
};
const CustomMessageStatus = () => {
const { message } = useMessageContext();
return <Text>{message.readBy}</Text>
};
...
<Channel MessageFooter={CustomMessageFooter} MessageStatus={CustomMessageStatus}>
It is important to keep in mind when creating custom components that some may have internal functionality you may want to keep. Additionally many of our out of the box component utilize custom memoization equality functions to minimize re-renders. With this in mind we suggest looking at the source code for any component you wish to replace, and possibly using it as a jumping off point, to ensure you are not removing desired functionality or performance.