diff --git a/docusaurus/docs/reactnative/core-components/chat.mdx b/docusaurus/docs/reactnative/core-components/chat.mdx index 59cf435ab5..2b71afb5cf 100644 --- a/docusaurus/docs/reactnative/core-components/chat.mdx +++ b/docusaurus/docs/reactnative/core-components/chat.mdx @@ -23,21 +23,45 @@ We recommend using only one instance of `Chat` provider per application unless a ```tsx import { StreamChat } from 'stream-chat'; -import { ChannelList, Chat, OverlayProvider } from 'stream-chat-react-native'; - -const client = StreamChat.getInstance('api_key'); - -export const App = () => ( - - // highlight-next-line - - +import { ChannelList, Chat, OverlayProvider, useCreateChatClient } from 'stream-chat-react-native'; + +// highlight-start +const chatApiKey = 'REPLACE_WITH_API_KEY'; +const chatUserId = 'REPLACE_WITH_USER_ID'; +const chatUserName = 'REPLACE_WITH_USER_NAME'; +const chatUserToken = 'REPLACE_WITH_USER_TOKEN'; +// highlight-end + +const user = { + id: chatUserId, + name: chatUserName, +}; + +export const App = () => { + // highlight-start + const chatClient = useCreateChatClient({ + apiKey: chatApiKey, + userData: user, + tokenOrProvider: chatUserToken, + }); + // highlight-end + + return ( + // highlight-next-line - - -); + + + // highlight-next-line + + + ); +}; ``` +:::tip +You can use the `useCreateChatClient` hook from `stream-chat-react-native`/`stream-chat-expo` to create a client instance and automatically connect/disconnect a user as per the example above, for simplicity. +::: + ## Context Providers `Chat` contains providers for the `ChatContext`, `ThemeContext`, and `TranslationContext`. diff --git a/docusaurus/docs/reactnative/ui-components/overview.mdx b/docusaurus/docs/reactnative/ui-components/overview.mdx index 29886c4d33..7105b63d4b 100644 --- a/docusaurus/docs/reactnative/ui-components/overview.mdx +++ b/docusaurus/docs/reactnative/ui-components/overview.mdx @@ -67,6 +67,10 @@ To disconnect a user you can call `disconnectUser` on the client. await client.disconnectUser(); ``` +:::tip +Alternatively, you can also use the `useCreateChatClient` hook from `stream-chat-react-native`/`stream-chat-expo` to create a client instance and automatically connect/disconnect a user. +::: + ## Creating a Channel Channels are at the core of Stream Chat, they are where messages are contained, sent, and interacted with. diff --git a/examples/ExpoMessaging/components/ChatWrapper.tsx b/examples/ExpoMessaging/components/ChatWrapper.tsx index 831fbb63f4..2b94215ffd 100644 --- a/examples/ExpoMessaging/components/ChatWrapper.tsx +++ b/examples/ExpoMessaging/components/ChatWrapper.tsx @@ -1,6 +1,5 @@ import React, { PropsWithChildren } from 'react'; -import { Chat, OverlayProvider, Streami18n } from 'stream-chat-expo'; -import { useChatClient } from '../hooks/useChatClient'; +import { Chat, OverlayProvider, Streami18n, useCreateChatClient } from 'stream-chat-expo'; import { AuthProgressLoader } from './AuthProgressLoader'; import { StreamChatGenerics } from '../types'; import { STREAM_API_KEY, user, userToken } from '../constants'; @@ -12,7 +11,7 @@ const streami18n = new Streami18n({ export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => { const { bottom } = useSafeAreaInsets(); - const chatClient = useChatClient({ + const chatClient = useCreateChatClient({ apiKey: STREAM_API_KEY, userData: user, tokenOrProvider: userToken, @@ -24,7 +23,7 @@ export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => { return ( bottomInset={bottom} i18nInstance={streami18n}> - + {children} diff --git a/examples/ExpoMessaging/hooks/useChatClient.tsx b/examples/ExpoMessaging/hooks/useChatClient.tsx deleted file mode 100644 index 73408bff0d..0000000000 --- a/examples/ExpoMessaging/hooks/useChatClient.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import {useEffect, useState} from 'react'; -import {StreamChat, OwnUserResponse, UserResponse} from 'stream-chat'; -import {StreamChatGenerics} from '../types'; - -export const useChatClient = < - SCG extends StreamChatGenerics = StreamChatGenerics, ->({ - apiKey, - userData, - tokenOrProvider, -}: { - apiKey: string; - userData?: OwnUserResponse | UserResponse; - tokenOrProvider?: string; -}) => { - const [chatClient, setChatClient] = useState | null>(null); - - useEffect(() => { - const client = new StreamChat(apiKey); - - if (!userData) { - return; - } - - let didUserConnectInterrupt = false; - let connectionPromise = client - .connectUser(userData, tokenOrProvider) - .then(() => { - if (!didUserConnectInterrupt) { - setChatClient(client); - } - }); - - return () => { - didUserConnectInterrupt = true; - setChatClient(null); - connectionPromise - .then(() => client.disconnectUser()) - .then(() => { - console.log('Connection closed'); - }); - }; - }, [apiKey, userData, tokenOrProvider]); - - return chatClient; -}; diff --git a/examples/TypeScriptMessaging/App.tsx b/examples/TypeScriptMessaging/App.tsx index 16859bd69f..44bd02155a 100644 --- a/examples/TypeScriptMessaging/App.tsx +++ b/examples/TypeScriptMessaging/App.tsx @@ -4,7 +4,7 @@ import { DarkTheme, DefaultTheme, NavigationContainer, RouteProp } from '@react- import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack'; import { useHeaderHeight } from '@react-navigation/elements'; import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'; -import { Channel as ChannelType, ChannelSort, StreamChat } from 'stream-chat'; +import { Channel as ChannelType, ChannelSort } from 'stream-chat'; import { Channel, ChannelList, @@ -18,12 +18,14 @@ import { Thread, ThreadContextValue, useAttachmentPickerContext, + useCreateChatClient, useOverlayContext, } from 'stream-chat-react-native'; import { useStreamChatTheme } from './useStreamChatTheme'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { useFlipper } from 'stream-chat-react-native-devtools'; +import { AuthProgressLoader } from './AuthProgressLoader'; LogBox.ignoreAllLogs(true); @@ -62,7 +64,7 @@ QuickSqliteClient.logger = (level, message, extraData) => { console.log(level, `QuickSqliteClient: ${message}`, extraData); }; -const chatClient = StreamChat.getInstance('q95x9hkbyd6p'); +const apiKey = 'q95x9hkbyd6p'; const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicm9uIn0.eRVjxLvd4aqCEHY_JRa97g6k7WpHEhxL7Z4K4yTot1c'; @@ -219,85 +221,82 @@ type AppContextType = { const AppContext = React.createContext({} as AppContextType); const App = () => { - const colorScheme = useColorScheme(); const { bottom } = useSafeAreaInsets(); const theme = useStreamChatTheme(); + const { channel } = useContext(AppContext); - const [channel, setChannel] = useState>(); - const [clientReady, setClientReady] = useState(false); - const [thread, setThread] = useState['thread']>(); - - useEffect(() => { - const setupClient = async () => { - const connectPromise = chatClient.connectUser(user, userToken); - setClientReady(true); - await connectPromise; - }; + const chatClient = useCreateChatClient({ + apiKey, + userData: user, + tokenOrProvider: userToken, + }); - setupClient(); - }, []); + if (!chatClient) { + return ; + } return ( - - - - - - bottomInset={bottom} - i18nInstance={streami18n} - value={{ style: theme }} - > - - {clientReady && ( - - ({ - headerBackTitle: 'Back', - headerRight: EmptyHeader, - headerTitle: channel?.data?.name, - })} - /> - - ({ headerLeft: EmptyHeader })} - /> - - )} - - - - - - + + bottomInset={bottom} + i18nInstance={streami18n} + value={{ style: theme }} + > + + + ({ + headerBackTitle: 'Back', + headerRight: EmptyHeader, + headerTitle: channel?.data?.name, + })} + /> + + ({ headerLeft: EmptyHeader })} + /> + + + ); }; export default () => { + const [channel, setChannel] = useState>(); + const [thread, setThread] = useState['thread']>(); const theme = useStreamChatTheme(); + const colorScheme = useColorScheme(); return ( - + + + + + + + + + ); }; diff --git a/examples/TypeScriptMessaging/AuthProgressLoader.tsx b/examples/TypeScriptMessaging/AuthProgressLoader.tsx new file mode 100644 index 0000000000..a98d5d643a --- /dev/null +++ b/examples/TypeScriptMessaging/AuthProgressLoader.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { ActivityIndicator, StyleSheet } from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +export const AuthProgressLoader = () => { + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); diff --git a/package/src/components/Chat/hooks/useCreateChatClient.ts b/package/src/components/Chat/hooks/useCreateChatClient.ts new file mode 100644 index 0000000000..f98af175a3 --- /dev/null +++ b/package/src/components/Chat/hooks/useCreateChatClient.ts @@ -0,0 +1,57 @@ +import { useEffect, useState } from 'react'; + +import { StreamChat } from 'stream-chat'; + +import type { + DefaultGenerics, + ExtendableGenerics, + OwnUserResponse, + StreamChatOptions, + TokenOrProvider, + UserResponse, +} from 'stream-chat'; + +/** + * React hook to create, connect and return `StreamChat` client. + */ +export const useCreateChatClient = ({ + apiKey, + options, + tokenOrProvider, + userData, +}: { + apiKey: string; + tokenOrProvider: TokenOrProvider; + userData: OwnUserResponse | UserResponse; + options?: StreamChatOptions; +}) => { + const [chatClient, setChatClient] = useState | null>(null); + const [cachedUserData, setCachedUserData] = useState(userData); + + if (userData.id !== cachedUserData.id) { + setCachedUserData(userData); + } + + const [cachedOptions] = useState(options); + + useEffect(() => { + const client = new StreamChat(apiKey, undefined, cachedOptions); + let didUserConnectInterrupt = false; + + const connectionPromise = client.connectUser(cachedUserData, tokenOrProvider).then(() => { + if (!didUserConnectInterrupt) setChatClient(client); + }); + + return () => { + didUserConnectInterrupt = true; + setChatClient(null); + connectionPromise + .then(() => client.disconnectUser()) + .then(() => { + console.log(`Connection for user "${cachedUserData.id}" has been closed`); + }); + }; + }, [apiKey, cachedUserData, cachedOptions, tokenOrProvider]); + + return chatClient; +}; diff --git a/package/src/components/index.ts b/package/src/components/index.ts index 3a43e98a15..abeb80568e 100644 --- a/package/src/components/index.ts +++ b/package/src/components/index.ts @@ -69,6 +69,7 @@ export * from './ChannelPreview/hooks/useChannelPreviewDisplayPresence'; export * from './ChannelPreview/hooks/useLatestMessagePreview'; export * from './Chat/Chat'; +export * from './Chat/hooks/useCreateChatClient'; export * from './Chat/hooks/useCreateChatContext'; export * from './Chat/hooks/useIsOnline'; export * from './Chat/hooks/useMutedUsers';