SDK de Mensagens para Clientes do Chat do IVS: Práticas recomendadas do React e do React Native
Este documento descreve as práticas mais importantes de uso do SDK de Mensagens do Chat do Amazon IVS para React e React Native. Essas informações devem permitir que você crie uma funcionalidade típica de chat dentro de uma aplicação React e forneça a base de que você precisa para se aprofundar nas partes mais avançadas do SDK de Mensagens do Chat do IVS.
Criar um gancho do inicializador do ChatRoom
A classe ChatRoom
contém os principais métodos de chat e receptores para gerenciar o estado da conexão e receber eventos, como mensagem recebida e mensagem excluída. Aqui, mostramos como armazenar adequadamente as instâncias de chat em um gancho.
Implementação
- TypeScript
-
// useChatRoom.ts
import React from 'react';
import { ChatRoom, ChatRoomConfig } from 'amazon-ivs-chat-messaging';
export const useChatRoom = (config: ChatRoomConfig) => {
const [room] = React.useState(() => new ChatRoom(config));
return { room };
};
- JavaScript
-
import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';
export const useChatRoom = (config) => {
const [room] = React.useState(() => new ChatRoom(config));
return { room };
};
Observação: não usamos o método dispatch
do gancho setState
porque não é possível atualizar parâmetros de configuração em tempo real. O SDK cria uma instância uma vez e não é possível atualizar o provedor do token.
Importante: use o gancho do inicializador do ChatRoom
uma vez para inicializar uma nova instância de sala de chat.
Exemplo
TypeScript/JavaScript:
// ...
const MyChatScreen = () => {
const userId = 'Mike';
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(ROOM_ID, ['SEND_MESSAGE']),
});
const handleConnect = () => {
room.connect();
};
// ...
};
// ...
Receptor para o estado da conexão
Opcionalmente, você pode se inscrever para receber atualizações do estado da conexão no gancho da sua sala de chat.
Implementação
- TypeScript
-
// useChatRoom.ts
import React from 'react';
import { ChatRoom, ChatRoomConfig, ConnectionState } from 'amazon-ivs-chat-messaging';
export const useChatRoom = (config: ChatRoomConfig) => {
const [room] = useState(() => new ChatRoom(config));
const [state, setState] = React.useState<ConnectionState>('disconnected');
React.useEffect(() => {
const unsubscribeOnConnecting = room.addListener('connecting', () => {
setState('connecting');
});
const unsubscribeOnConnected = room.addListener('connect', () => {
setState('connected');
});
const unsubscribeOnDisconnected = room.addListener('disconnect', () => {
setState('disconnected');
});
return () => {
unsubscribeOnConnecting();
unsubscribeOnConnected();
unsubscribeOnDisconnected();
};
}, []);
return { room, state };
};
- JavaScript
-
// useChatRoom.js
import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';
export const useChatRoom = (config) => {
const [room] = useState(() => new ChatRoom(config));
const [state, setState] = React.useState('disconnected');
React.useEffect(() => {
const unsubscribeOnConnecting = room.addListener('connecting', () => {
setState('connecting');
});
const unsubscribeOnConnected = room.addListener('connect', () => {
setState('connected');
});
const unsubscribeOnDisconnected = room.addListener('disconnect', () => {
setState('disconnected');
});
return () => {
unsubscribeOnConnecting();
unsubscribeOnConnected();
unsubscribeOnDisconnected();
};
}, []);
return { room, state };
};
Provedor de instâncias do ChatRoom
Para usar o gancho em outros componentes (para evitar prop drilling), você pode criar um provedor de sala de chat usando o context
React.
Implementação
- TypeScript
-
// ChatRoomContext.tsx
import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';
const ChatRoomContext = React.createContext<ChatRoom | undefined>(undefined);
export const useChatRoomContext = () => {
const context = React.useContext(ChatRoomContext);
if (context === undefined) {
throw new Error('useChatRoomContext must be within ChatRoomProvider');
}
return context;
};
export const ChatRoomProvider = ChatRoomContext.Provider;
- JavaScript
-
// ChatRoomContext.jsx
import React from 'react';
import { ChatRoom } from 'amazon-ivs-chat-messaging';
const ChatRoomContext = React.createContext(undefined);
export const useChatRoomContext = () => {
const context = React.useContext(ChatRoomContext);
if (context === undefined) {
throw new Error('useChatRoomContext must be within ChatRoomProvider');
}
return context;
};
export const ChatRoomProvider = ChatRoomContext.Provider;
Exemplo
Depois de criar o ChatRoomProvider
, você pode consumir sua instância com useChatRoomContext
.
Importante: só coloque o provedor no nível raiz se precisar acessar o context
entre a tela de chat e os outros componentes intermediários para evitar novas renderizações desnecessárias se você estiver recebendo conexões. Caso contrário, coloque o provedor o mais próximo possível da tela de chat.
TypeScript/JavaScript:
// AppContainer
const AppContainer = () => {
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(ROOM_ID, ['SEND_MESSAGE']),
});
return (
<ChatRoomProvider value={room}>
<MyChatScreen />
</ChatRoomProvider>
);
};
// MyChatScreen
const MyChatScreen = () => {
const room = useChatRoomContext();
const handleConnect = () => {
room.connect();
};
// ...
};
// ...
Criar um receptor de mensagem
Para se manter atualizado com todas as mensagens recebidas, você deve se inscrever em eventos de message
e deleteMessage
. Veja alguns códigos que fornecem mensagens de chat para seus componentes.
Importante: para fins de desempenho, separamos ChatMessageContext
de ChatRoomProvider
, pois podemos receber muitas novas renderizações quando o receptor do chat atualiza o estado da mensagem. Lembre-se de aplicar ChatMessageContext
nos componentes em que você usará ChatMessageProvider
.
Implementação
- TypeScript
-
// ChatMessagesContext.tsx
import React from 'react';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { useChatRoomContext } from './ChatRoomContext';
const ChatMessagesContext = React.createContext<ChatMessage[] | undefined>(undefined);
export const useChatMessagesContext = () => {
const context = React.useContext(ChatMessagesContext);
if (context === undefined) {
throw new Error('useChatMessagesContext must be within ChatMessagesProvider);
}
return context;
};
export const ChatMessagesProvider = ({ children }: { children: React.ReactNode }) => {
const room = useChatRoomContext();
const [messages, setMessages] = React.useState<ChatMessage[]>([]);
React.useEffect(() => {
const unsubscribeOnMessageReceived = room.addListener('message', (message) => {
setMessages((msgs) => [message, ...msgs]);
});
const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteEvent) => {
setMessages((prev) => prev.filter((message) => message.id !== deleteEvent.messageId));
});
return () => {
unsubscribeOnMessageDeleted();
unsubscribeOnMessageReceived();
};
}, [room]);
return <ChatMessagesContext.Provider value={messages}>{children}</ChatMessagesContext.Provider>;
};
- JavaScript
-
// ChatMessagesContext.jsx
import React from 'react';
import { useChatRoomContext } from './ChatRoomContext';
const ChatMessagesContext = React.createContext(undefined);
export const useChatMessagesContext = () => {
const context = React.useContext(ChatMessagesContext);
if (context === undefined) {
throw new Error('useChatMessagesContext must be within ChatMessagesProvider);
}
return context;
};
export const ChatMessagesProvider = ({ children }) => {
const room = useChatRoomContext();
const [messages, setMessages] = React.useState([]);
React.useEffect(() => {
const unsubscribeOnMessageReceived = room.addListener('message', (message) => {
setMessages((msgs) => [message, ...msgs]);
});
const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteEvent) => {
setMessages((prev) => prev.filter((message) => message.id !== deleteEvent.messageId));
});
return () => {
unsubscribeOnMessageDeleted();
unsubscribeOnMessageReceived();
};
}, [room]);
return <ChatMessagesContext.Provider value={messages}>{children}</ChatMessagesContext.Provider>;
};
Exemplo no React
Importante: lembre-se de encapsular seu contêiner de mensagens com o ChatMessagesProvider
. A linha Message
é um exemplo de componente que exibe o conteúdo de uma mensagem.
TypeScript/JavaScript:
// your message list component...
import React from 'react';
import { useChatMessagesContext } from './ChatMessagesContext';
const MessageListContainer = () => {
const messages = useChatMessagesContext();
return (
<React.Fragment>
{messages.map((message) => (
<MessageRow message={message} />
))}
</React.Fragment>
);
};
Exemplo no React Native
Por padrão, ChatMessage
contém id
, que é usado automaticamente como chaves do React em FlatList
para cada linha; portanto, não é preciso passar keyExtractor
.
- TypeScript
-
// MessageListContainer.tsx
import React from 'react';
import { ListRenderItemInfo, FlatList } from 'react-native';
import { ChatMessage } from 'amazon-ivs-chat-messaging';
import { useChatMessagesContext } from './ChatMessagesContext';
const MessageListContainer = () => {
const messages = useChatMessagesContext();
const renderItem = useCallback(({ item }: ListRenderItemInfo<ChatMessage>) => <MessageRow />, []);
return <FlatList data={messages} renderItem={renderItem} />;
};
- JavaScript
-
// MessageListContainer.jsx
import React from 'react';
import { FlatList } from 'react-native';
import { useChatMessagesContext } from './ChatMessagesContext';
const MessageListContainer = () => {
const messages = useChatMessagesContext();
const renderItem = useCallback(({ item }) => <MessageRow />, []);
return <FlatList data={messages} renderItem={renderItem} />;
};
Várias instâncias de sala de chat em uma aplicação
Se você usa várias salas de chat simultâneas na sua aplicação, propomos a criação de cada provedor para cada chat e consumi-lo no provedor de chat. Neste exemplo, estamos criando um chat de bot de ajuda e de suporte ao cliente. Criamos um provedor para ambos.
- TypeScript
-
// SupportChatProvider.tsx
import React from 'react';
import { SUPPORT_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';
export const SupportChatProvider = ({ children }: { children: React.ReactNode }) => {
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(SUPPORT_ROOM_ID, ['SEND_MESSAGE']),
});
return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
// SalesChatProvider.tsx
import React from 'react';
import { SALES_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';
export const SalesChatProvider = ({ children }: { children: React.ReactNode }) => {
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(SALES_ROOM_ID, ['SEND_MESSAGE']),
});
return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
- JavaScript
-
// SupportChatProvider.jsx
import React from 'react';
import { SUPPORT_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';
export const SupportChatProvider = ({ children }) => {
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(SUPPORT_ROOM_ID, ['SEND_MESSAGE']),
});
return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
// SalesChatProvider.jsx
import React from 'react';
import { SALES_ROOM_ID, SOCKET_URL } from '../../config';
import { tokenProvider } from '../tokenProvider';
import { ChatRoomProvider } from './ChatRoomContext';
import { useChatRoom } from './useChatRoom';
export const SalesChatProvider = ({ children }) => {
const { room } = useChatRoom({
regionOrUrl: SOCKET_URL,
tokenProvider: () => tokenProvider(SALES_ROOM_ID, ['SEND_MESSAGE']),
});
return <ChatRoomProvider value={room}>{children}</ChatRoomProvider>;
};
Exemplo no React
Agora, você pode usar diferentes provedores de chat que usam o mesmo ChatRoomProvider
. Posteriormente, você poderá reutilizar o mesmo useChatRoomContext
dentro de cada tela/visualização.
TypeScript/JavaScript:
// App.tsx / App.jsx
const App = () => {
return (
<Routes>
<Route
element={
<SupportChatProvider>
<SupportChatScreen />
</SupportChatProvider>
}
/>
<Route
element={
<SalesChatProvider>
<SalesChatScreen />
</SalesChatProvider>
}
/>
</Routes>
);
};
Exemplo no React Native
TypeScript/JavaScript:
// App.tsx / App.jsx
const App = () => {
return (
<Stack.Navigator>
<Stack.Screen name="SupportChat">
<SupportChatProvider>
<SupportChatScreen />
</SupportChatProvider>
</Stack.Screen>
<Stack.Screen name="SalesChat">
<SalesChatProvider>
<SalesChatScreen />
</SalesChatProvider>
</Stack.Screen>
</Stack.Navigator>
);
};
TypeScript/JavaScript:
// SupportChatScreen.tsx / SupportChatScreen.jsx
// ...
const SupportChatScreen = () => {
const room = useChatRoomContext();
const handleConnect = () => {
room.connect();
};
return (
<>
<Button title="Connect" onPress={handleConnect} />
<MessageListContainer />
</>
);
};
// SalesChatScreen.tsx / SalesChatScreen.jsx
// ...
const SalesChatScreen = () => {
const room = useChatRoomContext();
const handleConnect = () => {
room.connect();
};
return (
<>
<Button title="Connect" onPress={handleConnect} />
<MessageListContainer />
</>
);
};