@chatscope/use-chat

中文文档
3.1.2发布于 2023/09/27MITTypeScriptCommonJS近 30 天下载 7.2万

React hook for state management in chat applications

原始使用文档
文档目录12 节
  1. What is it?
  2. Why?
  3. Features
  4. Installation
  5. Architecture
  6. BasicStorage
  7. ExampleChatService
  8. Service factory
  9. Basic usage
  10. Community and support
  11. Website
  12. License

Use Chat

Actions Status npm version code style: prettier semantic-release

!!! Please do not use version 1.5.0 it has wrong property names !!!

React hook for state management in chat applications.

Full documentation is not available yet, but will be prepared.

If you are interested in this library and need more documentation, please let me know by adding one of the positive reactions (+1, Heart, Rocket) in the dedicated issue here: https://github.com/chatscope/use-chat/issues/1

What is it?

This is a headless chat library. Think of it as something like a Formik but for chat apps.

The library can be used both with @chatscope/chat-ui-kit-react as well as with other chat components.

The goal of it is to provide a tool for handling features that are most often implemented in chat applications.
Primarily it's an application state management as well as some nice addons such as debounce or throttling of sending or receiving typing indicator signaling.

Why?

The logic of chat applications is often repetitive. Most of these applications contains a user list, a conference list and of course messages. There are at least a few chat API providers on the market. They provide saas services that you can use to build a chat application. You also almost always get a ready-to-use, simple messaging library from them. Such a library encapsulates a complex communication protocol.
Sometimes you also get UI components and hooks/providers that bind the UI to the provider's messaging library. Both are closely related, which makes replacing the messaging library or UI components a lot of work.

In this case you have to take care of how to keep in your app: a list of contacts and theirs statuses, a list of messages for each contact, switching between chats, setting an active chat, rendering messages and so on...

If you create your own backed and implement the communication layer yourself, you will also not avoid doing all these things.

Isn't it better to focus on the business functionality of the application? On the layout, colour selection etc., instead of wondering how to find a message in the array to set its state to "read"?

Such things often turn out to be more complicated to do than they seem.

For example, a message list is not always a flat array. It will often be an object with messages assigned to users and conversations, and additionally, it will be grouped into blocks of incoming and outgoing messages.

Adding a message to the list requires roughly the following steps:

  • find a message list for a given conversation,
  • find the last group,
  • check if this is the group of the user the message comes from,
  • if it's not, then create a group and add the message to it
  • add the group to the list

Uff, there is a bit of it, right?

Such things should be closed in separate logic libraries, which can be used in a simple and intuitive way. The implementation of a consistent interface also provides the possibility of internal tuning (e.g. changing the data structure) without changing the way the library is used.

Features

  • handling users states (with user business properties such as name, avatar, presence etc.)
  • handling conversations state (including current conversation)
  • handling grouped messages state for many conversations
  • sending debounced typing indicator
  • auto throttling received typing indicator
  • handling current message input value
  • handling message drafts
  • updating existed messages
  • support for any communication service

Installation

Using yarn.

yarn add @chatscope/use-chat

Using npm.

npm install @chatscope/use-chat

Architecture

The library consist of three parts:

  • storage - the place where the state is kept
  • ChatProvider - chat context provider
  • useChat - hook that binds storage service and UI

BasicStorage

This is a class that implements the IStorage interface. All data such as conversations, messages, current conversation indicator etc sits in the storage. BasicStorage is the basic implementation of the IStorage. It should be fully functional, and can be used for most applications. However, it is possible to write new implementations e.g. based on redux or another state library. That is why the storage is provided to ChatProvider from the outside.

ExampleChatService

This is a class that implements IChatService interface. The purpose of this service is to maintain a connection with the chat server, send and receive messages and chat protocol commands. This is a point that connects your chat server with the library. To use this library, you need to write your own ChatService that implements the IChatService interface. The implementation of the service depends on which chat server you are using. The content of the service can be your code written from scratch, but the service also can be an encapsulation layer for any ready to use chat communication library
There is src/examples/ExampleChatService.ts available for a quick start. This is a good starting point for developing the real service for your application.

At the future I will provide more examples showing real communication with socket.io based chat server.

Service implementations for some SAAS providers will also be available.

Service factory

It is a simple function that you need to implement yourself. This function receives a chat storage object (IChatStorage) as the first argument and update function as the second. This function must return an instance of the class implementing IChatService. Sometimes in your ChatService implementation, you will need to get values from the storage as well as save them to the storage. Here you can pass the storage to your service. Access to the storage state is provided by storage.getState() method. Writing to the storage is realized by calling functions such as storage.addConversation(), storage.addUser() etc. The second argument is the updateState function. Each write to the storage performed from the service needs to call the updateState function to perform re-render. For example when the service receives signalization that some user has connected, you can add user to storage using storage.addUser() method and next call updateState().

This description probably looks complicated :). But believe, me it's really simple compared to when you have to take care of everything.

Basic usage

This is very simple example, but it shows how easy is it to implement a chat using useChat hook.

For more complex example based on CRA please visit https://github.com/chatscope/use-chat-example. Working example app is available here: https://use-chat.examples.chatscope.io

File: index.js

import {nanoid} from "nanoid";
import {
  BasicStorage,
  ChatProvider,
  ExampleChatService,
  AutoDraft
} from "@chatscope/use-chat";

// Storage needs to generate id for messages and groups
const messageIdGenerator = () => nanoid();
const groupIdGenerator = () => nanoid();

// Create serviceFactory
const serviceFactory = (storage, updateState) => {
  return new ExampleChatService(storage, updateState);
};

const chatStorage = new BasicStorage({groupIdGenerator, messageIdGenerator});

export const App = () => {
  return (
    <ChatProvider serviceFactory={serviceFactory} storage={chatStorage} config={{
      typingThrottleTime: 250,
      typingDebounceTime: 900,
      debounceTyping: true,
      autoDraft: AutoDraft.Save | AutoDraft.Restore
    }}>
      <Chat />
    </ChatProvider>
  );
};

File Chat.js:

import {useState, useMemo } from "react";
import { MainContainer, Sidebar, ConversationList, Conversation, Avatar, MessageGroup, Message,
  ChatContainer, ConversationHeader, MessageList, MessageInput} from "@chatscope/chat-ui-kit-react";
import { useChat, ChatMessage, MessageContentType, MessageDirection, MessageStatus } from "@chatscope/use-chat";

export const Chat = () => {

  // Message input value
  const [value, setValue] = useState("");

  // Get all chat related values and methods from useChat hook 
  const {
    currentMessages, conversations, activeConversation, setActiveConversation, sendMessage, getUser
  } = useChat();

  // Get current user data
  const [currentUserAvatar, currentUserName] = useMemo(() => {

    if (activeConversation) {
      const participant = activeConversation.participants.length > 0 ? activeConversation.participants[0] : undefined;

      if (participant) {
        const user = getUser(participant.id);
        if (user) {
          return [<Avatar src={user.avatar} />, user.username]
        }
      }
    }

    return [undefined, undefined];

  }, [activeConversation]);

  const handleSend = text => {

    // Logger user (sender)
    const currentUserId = "123";

    const message = new ChatMessage({
      id: "",
      content: text,
      contentType: MessageContentType.TextHtml,
      senderId: currentUserId,
      direction: MessageDirection.Outgoing,
      status: MessageStatus.Sent
    });

    sendMessage({
      message,
      conversationId: activeConversation.id,
      senderId: currentUserId,
    });

  };

  return (<MainContainer>
    <Sidebar position="left">
      <ConversationList>
        {conversations.map(c => {
          
          // Helper for getting the data of the first participant
          const [avatar, name] = (() => {

            const participant = c.participants.length > 0 ? c.participants[0] : undefined;
            if (participant) {
              const user = getUser(participant.id);
              if (user) {

                return [<Avatar src={user.avatar} />, user.username]

              }
            }

            return [undefined, undefined]
          })();

          return (<Conversation key={c.id}
                        name={name}
                        active={activeConversation?.id === c.id}
                        unreadCnt={c.unreadCounter}
                        onClick={e => setActiveConversation(c.id)}>
            {avatar}
          </Conversation>);
        })}
      </ConversationList>
    </Sidebar>
    
    <ChatContainer>
      
      <ConversationHeader>
        {currentUserAvatar}
        <ConversationHeader.Content userName={currentUserName} />
      </ConversationHeader>
      
      <MessageList>
        {currentMessages.map(g => <MessageGroup key={g.id} direction={g.direction}>
          <MessageGroup.Messages>
            {g.messages.map(m => <Message key={m.id} model={{
              type: "text",
              payload: m.content
            }} />)}
          </MessageGroup.Messages>
        </MessageGroup>)}
      </MessageList>
      
      <MessageInput value={value} onSend={handleSend} />
      
    </ChatContainer>
    
  </MainContainer>);
}

Community and support

Website

https://chatscope.io

License

MIT

版本与发布记录

@chatscope/use-chat 共发布 36 个版本,首次发布于 2021/05/10;过去 12 个月发布了 0 个版本。

版本发布日期距今
3.1.2latest2023/09/273 年前
4.0.0-alpha.9alpha2023/03/093 年前
4.0.0-alpha.82023/03/093 年前
4.0.0-alpha.72023/03/093 年前
4.0.0-alpha.62023/03/093 年前
4.0.0-alpha.52023/03/093 年前

关于 @chatscope/use-chat 的常见问题

@chatscope/use-chat 是什么?

React hook for state management in chat applications

如何安装 @chatscope/use-chat?

在项目目录运行 npm install @chatscope/use-chat,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>= 17.0 <= 18.2.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@chatscope/use-chat 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@chatscope/use-chat 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@chatscope/use-chat 可以商用吗?

@chatscope/use-chat 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@chatscope/use-chat 的下载量有多少?

根据 npm 官方下载统计,@chatscope/use-chat 在 2026/10/10 – 2026/11/08 这 30 天内被下载 71,619 次,日均约 2,387 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@chatscope/use-chat 还在维护吗?

最新版本 3.1.2 发布于 2023/09/27(3 年前);至今共发布 36 个版本,其中过去 12 个月发布了 0 个;首次发布于 2021/05/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。