使用React,TypeScript和Socket.io构建聊天应用

This is going to be a thorough step-by-step guide for building a single page chat application using React, TypeScript and Socket.io.

这将是使用React,TypeScript和Socket.io构建单页聊天应用程序的详尽分步指南。

If you want to skip the reading, here ? is the GitHub repository with a detailed README, and here you can check the live demo. In order to play with it, you need to open it in two different browsers (or browser tabs) or devices (you may use your computer and your smartphone) and chat with each other.

如果您想跳过阅读, 在这里 ? 是带有详细自述文件的GitHub存储库, 您可以在此处查看实时演示。 为了使用它,您需要在两个不同的浏览器(或浏览器选项卡)或设备(可以使用计算机和智能手机)中打开它并彼此聊天。

研究 (Research)

When you are about to start a new project, it’s a good practice to do initial research about the technical stack you are planning to use.

当您将要开始一个新项目时,对打算使用的技术堆栈进行初步研究是个好习惯。

In other words, you may want or need — especially if you don’t have prior experience with it — to investigate on each technology you will be using. I recommend doing that separately. Take one of the them and create a small app that you can play with.

换句话说,您可能想要或需要-特别是如果您没有使用它的经验-来研究将要使用的每种技术。 我建议分开做。 选择其中一个,然后创建一个可以玩的小应用程序。

If you need to check how the integration of two or more technologies is going to work in a real project — then you might want to include them all together in your “research-test-play” app — but preferably do your research one at a time.

如果您需要检查两种或多种技术的集成将如何在一个实际项目中工作-那么您可能希望将它们全部整合到您的“ 研究-测试-播放 ”应用程序中-但最好是在一个实验室中进行一项研究。时间。

达到重点 (Getting to the point)

When I started thinking about making this chat application, I did exactly what I described above. I haven’t had recent experience with TypeScript and none with Socket.io, so I had to take a look at those and get myself familiarized with what is their current state. As my plan was to use React as a main UI library, I needed to see how it was going to work with the other guys in the equation. So I did.

当我开始考虑制作此聊天应用程序时 ,我完全按照上面的描述进行了操作。 我最近没有使用TypeScript的经验,也没有Socket.io的经验,所以我不得不看一看,并熟悉它们的当前状态。 当我计划将React用作主要的UI库时,我需要了解它如何与方程式中的其他人一起工作。 所以我做了。

I created two small applications (repos here and here) with these technologies, just to be able to play with them and learn how could I use them in my future chat application.

我使用这些技术创建了两个小型应用程序( 此处和此处的仓库),以便能够使用它们并了解如何在未来的聊天应用程序中使用它们。

After my initial research was done I was able to start thinking and planning the implementation of my main chat app.

初步研究完成后,我便可以开始思考和计划主要聊天应用程序的实施。

高层规划 (High level planning)

Usually what people mean when they say “high level plan” is that they are looking for the big picture. Which means we need to create a rough plan of our execution and define our main pillars, but without going into too much detail. Now when we have clear idea of what to do, let’s start doing it! ?

通常,人们说“ 高级计划”的意思是他们正在寻找全局 。 这意味着我们需要为执行制定一个粗略的计划并定义我们的主要Struts,但又不必太详细。 现在,当我们清楚地知道要做什么时,让我们开始吧! ?

Note: From this point forward I will be assuming that you are following my steps as I describe them, hence I will be writing in the second person. ?

注意:从现在开始,我将假设您正在按照我描述的步骤进行操作,因此,我将以第二人称写作。

技术栈 (Tech stack)

We already mentioned the main technologies we will be using, but let’s define a proper list of all of them here:

我们已经提到了将要使用的主要技术,但是让我们在这里定义所有这些技术的正确列表:

  • React with TypeScript (create-react-app my-app --scripts-version=react-scripts-ts) — a UI library we will use for building our application’s user interfaces.

    使用TypeScript create-react-app my-app --scripts-version=react-scripts-ts ( create-react-app my-app --scripts-version=react-scripts-ts )—一个UI库,我们将使用它来构建应用程序的用户界面。

  • Redux — a state management library we will use for managing our application’s state.

    Redux —状态管理库,我们将使用它来管理应用程序的状态。

  • Express.js — Node.js web application framework we will use for creating an http server that we will need in our application, in order to take advantage of Socket.io engine.

    Express.js -Node.js Web应用程序框架,我们将使用它来创建我们的应用程序中需要的http服务器 ,以利用Socket.io引擎。

  • Socket.io — a JavaScript library for real time web applications. It enables real time, bi-directional communication between web clients and servers. We will use it to implement a simple chat behavior in our app.

    Socket.io —一个用于实时Web应用程序JavaScript库。 它支持Web客户端和服务器之间的实时双向通信。 我们将使用它在我们的应用程序中实现简单的聊天行为。

  • styled-components — a small library that we will be using for adding styles to our app and make the look and feel beautiful. It utilizes tagged template literals to style your components and removes the mapping between components and styles. This means that when you’re defining your styles, you’re actually creating a normal React component that has your styles attached to it.

    样式化的组件 -一个小的库,我们将使用它来为我们的应用程序添加样式,并使外观看起来更漂亮。 它利用带标签的模板文字来为您的组件设置样式,并删除组件和样式之间的映射。 这意味着在定义样式时,实际上是在创建一个普通的React组件,并附加了样式。

  • Jest/Enzyme — a JavaScript Testing Framework and a JavaScript Testing Utility that we will be using to write unit tests for our application. Both have great integration into the React ecosystem and are heavily used in real projects.

    Jest / Enzyme —我们将使用JavaScript测试框架和JavaScript测试实用程序为应用程序编写单元测试。 两者都很好地集成到了React生态系统中,并在实际项目中大量使用。

应用特点 (Application Features)

In this section we will describe what the features of our application are going to be.

在本节中,我们将描述应用程序的功能。

Every time when we plan a new project, we must define certain criteria which will describe some level of completion when met.

每当我们计划一个新项目时,我们都必须定义某些标准,这些标准将描述满足时的某种完成水平。

In other words, we need to set a limit point which, once reached, will show that our project is completed or at least in its first version. There is a famous saying, that could be matched to the issue with the “never ending” projects:

换句话说,我们需要设置一个极限点,一旦达到极限,它将显示我们的项目已完成或至少在其第一个版本中。 有句名言,可以与“永无止境”项目相提并论:

“A good plan today is better than a perfect plan tomorrow. — or we may say that a working project today is better than a perfect project tomorrow.

“今天好的计划比明天的完美计划要好。 ,或者我们可以说今天的工作项目比明天的完美项目要好。

Here is my list with the features I wanted to implement initially:

这是我最初要实现的功能的清单:

  • Chat tab — blinking when new message is received until it is read, or when the user is on Settings page

    “聊天”选项卡—在收到新消息直到被阅读之前或用户位于“设置”页面时闪烁
  • Settings tab

    设置标签
  • Unread messages counter

    未读邮件计数器
  • Font Awesome icons

    字体真棒图标

聊天页面 (Chat page)

  • Chat area (includes left aligned and right aligned messages)

    聊天区域(包括左对齐和右对齐的消息)
  • Message (text, datetime, left or right depending on if it’s received or sent)

    消息(文本,日期时间,向左或向右,取决于是否接收或发送)
  • Showing the nickname only of the sender

    仅显示发件人的昵称
  • Message sender — input field and button. Input is cleared and focused when button is clicked

    消息发件人-输入字段和按钮。 单击按钮时,输入将被清除并集中显示
  • Send messages with CTRL+ENTER

    使用CTRL + ENTER发送消息
  • Auto scroll to bottom when the chat area is not enough to show all messages

    当聊天区域不足以显示所有消息时,自动滚动到底部

设定页面 (Settings page)

  • UserProfile component — possibility to change user name

    UserProfile组件-更改用户名的可能性
  • Interface color component — change the color theme of the app

    界面颜色组件-更改应用程序的颜色主题
  • ClockDisplay component — change the time mode 12h or 24h, shown with each message

    ClockDisplay组件—更改时间模式12h或24h,与每个消息一起显示
  • Send messages with Ctrl+Enter — On/Off

    使用Ctrl + Enter发送邮件-开启/关闭
  • LanguageSwitcher — drop down menu allowing changing the language of the app (currently English and Deutsch are supported)

    LanguageSwitcher —下拉菜单允许更改应用程序的语言(当前支持英语和德语)
  • Reset button — resets all settings stored to local storage

    重置按钮—将所有设置重置为本地存储

ImprovementsAt the time of writing this, there are still some pending features I would like to implement. Below is the list of all improvements I did or plan to do in future (the ones with the thumb emoji are already implemented):

改进在撰写本文时,我仍然想实现一些尚待解决的功能。 以下是我将来所做或计划进行的所有改进的列表(已经实现了拇指表情符号的那些改进):

  • Add video chat feature.

    添加视频聊天功能。
  • ? Added AM/PM time formatting for when 12h mode is selected.

    ? 添加了选择12h模式时的AM / PM时间格式。
  • ? Added possibility to send message via ENTER by default. If the setting to send messages with CTRL+ENTER is ON, then this is going to be the only way (except via mouse/touch of course).

    ? 默认情况下增加了通过ENTER发送消息的可能性。 如果使用CTRL + ENTER发送消息的设置为ON,则这将是唯一的方法(当然,通过鼠标/触摸除外)。
  • ? Optimized for iDevices (media queries).

    ? 针对iDevices(媒体查询)进行了优化。
  • ? Fix blinking/active class for the Chat tab issue — related to React Router not able to properly re-render connected components https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md

    ? 修复“聊天”选项卡问题的闪烁/活动类-与React Router无法正确地重新渲染连接的组件有关。ttps://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs /guides/blocked-updates.md

  • ? Clear input field when new message is sent.

    ? 发送新消息时清除输入字段。
  • ? Auto scroll to bottom main chat area when new messages exceed available space.

    ? 当新消息超过可用空间时,自动滚动到底部的主要聊天区域。
  • ? Prevent ‘doubling messages’ (or multiple messages duplicates when more clients are connected).

    ? 防止“消息加倍”(或在连接更多客户端时多条消息重复)。
  • ? Add unit tests for the react components.

    ? 添加React组件的单元测试。
  • Add unit tests for redux stuff — reducers, store, action creators.

    添加用于redux的单元测试-减速器,商店,动作创建者。
  • ? Add media queries for responsiveness — test and adjust on more devices.

    ? 添加媒体查询以提高响应能力—在更多设备上进行测试和调整。
  • ? Add demo to Heroku.

    ? 将演示添加到Heroku。
  • ? Add nice how-to in README.

    ? 在自述文件中添加不错的方法。
  • Add animations for the messages.

    为消息添加动画。
  • Add sounds (with options to turn on/off in settings).

    添加声音(具有用于打开/关闭设置的选项)。
  • Add more color themes.

    添加更多颜色主题。
  • Add welcome message (broadcasts when a new user is connected).

    添加欢迎消息(在连接新用户时广播)。
  • ? Add icons (use Font Awesome).

    ? 添加图标(使用Awesome字体)。
  • History of all the conversations.

    所有对话的历史记录。
  • Handle case when socket’s connection state change (visually).

    当套接字的连接状态改变时(视觉上)处理外壳。
  • Handle case when there has been a socket error.

    处理出现套接字错误的情况。
  • Handle case when a very long word (without) spaces is entered and it goes beyond the message background color.

    当输入的单词很长(没有空格)并且超出消息背景颜色时,请处理这种情况。
  • ? Emoticons support — such as :D, :P, :), ;), ?, ❤️, etc.

    ? 支持表情符号,例如:D,:P,:),;),?,❤️等。
  • ? Link Parser — Youtube link (embedded video should appear), link to an image (embedded image should appear), all other links should appear as anchor.

    ? 链接解析器-Youtube链接(应显示嵌入的视频),链接到图像(应显示嵌入的图像),所有其他链接应显示为锚点。

When we know the initial plan and the requirements we need to fulfill, we can do our high level analyses. Our app will have two pages, Chat and Settings, accessible via tab controls.

当我们知道了初始计划和需要满足的要求时,便可以进行高级分析。 我们的应用程序将包含两个页面,即“聊天”和“设置”,可通过标签控件访问。

The Chat page will contain the main chat area with the controls needed to send messages (input field and a button).

聊天页面将包含主要聊天区域,其中包含发送消息所需的控件(输入字段和按钮)。

The Settings page will contain a few controls for selecting the options described above.

“设置”页面将包含一些用于选择上述选项的控件。

With that in mind we can go to the next section where we will create a more detailed plan before the actual implementation.

考虑到这一点,我们可以转到下一部分,在实际实施之前,我们将创建一个更详细的计划。

更详细的计划 (More detailed planning)

In this section we need to have a deeper look at our application and define what will be the building blocks for it. Since we are going to use React and we know that in the React world the term component is widely used, we may refer to our building blocks as components. We will have components responsible for purely visual stuff, as well as such for managing the local storage, for example.

在本节中,我们需要更深入地研究我们的应用程序,并定义将要为其构建的模块。 由于我们将要使用React,并且我们知道在React世界中术语“ 组件”已被广泛使用,因此我们可以将构建基块称为组件。 例如,我们将拥有负责纯粹视觉内容的组件,以及用于管理本地存储的组件。

Let’s try to imagine mentally how our app will look in the end and what components it will need. What we already know is this:

让我们试着从脑海中想象我们的应用程序最终的外观以及它将需要哪些组件。 我们已经知道的是:

服务器部分 (Server part)

We will need an HTTP server that will take care of starting the server and handling interactions with Socket.io (sending and receiving messages). Our server logic will be simple enough to live in only one file. You can see the actual implementation here.

我们将需要一个HTTP服务器,它将负责启动服务器并处理与Socket.io的交互(发送和接收消息)。 我们的服务器逻辑将非常简单,只需一个文件即可。 您可以在此处查看实际的实现。

Client partHere we need to have all the visual controls, plus means for managing interactions with local storage, where we will save the user preferences, as well as handling of the translations and color themes.

客户部分在这里,我们需要所有的视觉控件,以及用于管理与本地存储的交互的方法,在这里我们将保存用户首选项以及翻译和颜色主题的处理。

Now is a good moment to point out that for implementing the translations and theming functionality in the app, I have used React Context API. Also, since I knew I would have to deal with Local Storage, I did another round of the “research-test-play” trip. And the output of it was that I already had a nice service, which provides all the functionalities I needed.

现在是要指出的一个好时机,为了在应用程序中实现翻译和主题功能,我使用了React Context API 。 另外,由于我知道必须与本地存储打交道,所以我进行了另一轮 “ 研究测试”之旅。 结果是我已经有了一个不错的服务 ,它提供了我需要的所有功能。

Another thing you will notice when looking at the components folder is that every component has its own directory with a few files in it.

当您查看components文件夹时,您会注意到的另一件事是每个组件都有其自己的目录,其中包含一些文件。

These files serve the following logic:

这些文件具有以下逻辑:

index.ts → entry point, just expose the component itself. This helps for not having to write repeatedly and long import statements. Here is an example:

index.ts →入口点,仅公开组件本身。 这有助于不必重复编写冗长的import语句。 这是一个例子:

// Instead of having to write this:
import ChatArea from '../../ChatArea/ChatArea';

// We can have just this:
import ChatArea from '../../ChatArea';

.tsx (ChatArea.tsx) → actual component implementation live here.

.tsx(ChatAr ea.tsx)→实际的组件实现在此处。

.test.tsx (ChatArea.test.tsx) unit tests of the component live here.

.tes t.tsx(ChatArea.tes t.t sx) 组件的单元测试位于此处。

;.tsx (StyledChatArea.tsx) → CSS styles of the component live here.

; .tsx(StyledChatArea.tsx)→组件CSS样式位于此处。

The same pattern is used for most of the components, exception are only the pages, such as the components which play the role of parents for all the inner parts — ChatPage and SettingsPage.

大多数组件都使用相同的模式,只有页面例外,例如在所有内部部分( ChatPage和SettingsPage)中充当父级角色的组件。

So, with that said, I think we can see what would be our application structure when we try to “componentize” it. Here a list of the components I came up with:

因此,我认为当我们尝试“组件化”它时,我们可以看到我们的应用程序结构是什么。 这里列出了我想到的组件 :

Note: all names are a matter of personal choice, feel free to name yours as you wish.

注意:所有名称都是个人选择,请随意命名。

Let me try to give you a bit more detailed explanation for each of them below:

让我尝试为您提供以下每个方面的详细说明:

  • AppRouter — contains the main app routing logic. For instance, here we define the app routes by giving them the path and component to load when this path is reached. Uses React Router package.

    AppRouter —包含主要的应用程序路由逻辑。 例如,在这里,我们通过给应用路径指定路径和到达该路径时要加载的组件来定义它们。 使用React Router软件包。

  • ChatArea — represents the main chat area, where all the messages are being displayed. It’s responsible also for auto scrolling down when the visible area limit is reached.

    ChatArea-代表主要聊天区域,所有消息均在该区域显示。 当达到可见区域限制时,它还负责自动向下滚动。

  • ClockModeSelector — responsible for displaying controls allowing the user to select the time display mode -12h or 24h. It uses a common component called RadioGroup (will describe it below) and the Local Storage service to write/read from the browser’s local storage.

    ClockModeSelector-负责显示控件,允许用户选择时间显示模式-12h或24h。 它使用一个称为RadioGroup的通用组件(下面将对其进行描述)和本地存储服务来从浏览器的本地存储中进行写入/读取。

  • common/RadioGroup — this is a common component, built with the idea to be re-usable all over the app. We use this component in a few other components, such as ClockModeSelector, ThemeSelector and SendingOptions. It contains logic for displaying two radio buttons with the possibility to pass a callback function which will execute a certain action depending on your needs.

    common / RadioGroup-这是一个通用组件,其构想是可以在整个应用程序中重复使用。 我们在其他一些组件中使用此组件,例如ClockModeSelector, ThemeSelector和SendingOptions 。 它包含用于显示两个单选按钮的逻辑,并可以传递一个回调函数,该回调函数将根据您的需要执行某些操作。

  • LanguageSelector — responsible for displaying a select input control for choosing the app language. It accepts a function that is coming from the TranslationsProvider utility and does the actual language change.

    LanguageSelector-负责显示用于选择应用程序语言的选择输入控件。 它接受来自TranslationsProvider实用程序的功能并进行实际的语言更改。

  • Message — this component is responsible for displaying each chat message, sent or received. It includes the nickname of the sender and timestamp showing the time when the message was sent/received. It also provides support for emojis (like ❤️) and links parsing — see the screenshot below.

    消息 -此组件负责显示已发送或已接收的每个聊天消息。 它包括发件人的昵称和时间戳,显示发送/接收消息的时间。 它还提供对表情符号(例如❤️)和链接解析的支持-请参见下面的屏幕截图。

  • MessageSender — this is the component that provides the necessary user interface controls for sending messages — a text input field and a Send button. It contains logic for defining the different ways of sending — via click or key press (with ENTER or CTRL+ENTER), as well as clearing the input field when a new message is sent.

    MessageSender —这是提供用于发送消息的必要用户界面控件的组件—文本输入字段和“发送”按钮。 它包含用于定义不同发送方式的逻辑-通过单击或按键(使用ENTER或CTRL + ENTER),以及在发送新消息时清除输入字段。

  • Navigation — here lives the implementation of the app navigation. It consists of two tabs — Chat and Settings and contains logic for connecting to sockets, by sending a Redux action when the component is mounted. It manages an UnreadMessagesCounter component by passing it the count of the currently unread messages (this happens when the user receives a message while being on the Settings page). It also has a logic responsible for making the tab blink when a new message arrives.

    导航 -这里包含应用程序导航的实现。 它由两个选项卡(“ 聊天”“设置”)组成,并包含通过在安装组件时发送R edux操作来连接至套接字的逻辑。 它通过向其传递当前未读消息的计数来管理UnreadMessagesCounter组件(当用户在“设置”页面上收到消息时会发生这种情况)。 它还具有负责使新消息到达时选项卡闪烁的逻辑。

  • Nickname — this is simple component for rendering the nickname of a chat user.

    昵称 -这是用于呈现聊天用户昵称的简单组件。

  • ResetButton — this will be a simple component, used in the Settings page for rendering a Reset button. The function is going to be exactly that — resetting the settings selections that are already saved into the local storage, if any.

    ResetButton —这是一个简单的组件,在“ 设置”页面中用于呈现“重置”按钮。 该功能将完全是这样-重置已经保存到本地存储中的设置选择(如果有)。

  • SendingOptions — responsible for displaying options for choosing if a message can be sent via CTRL+ENTER. It works same way as ClockModeSelector component — uses RadioGroup component and accepts a callback function.

    SendingOptions-负责显示用于选择是否可以通过CTRL + ENTER发送消息的选项。 它的工作方式与ClockModeSelector组件相同—使用RadioGroup组件并接受回调函数。

  • ThemeSelector — same as the component above. The only difference is that here the user is allowed to select a color theme. In our case the options are only two — light theme or dark theme.

    ThemeSelector-与上面的组件相同。 唯一的区别是,此处允许用户选择颜色主题。 在我们的情况下,选项只有两个-浅色主题或深色主题。

  • Timestamp — simple component used for rendering the time of the messages.

    时间戳记 —用于呈现消息时间的简单组件。

  • UnreadMessagesCounter — this is the component I mentioned a little bit earlier. It shows a counter indicating the number of the received, but not yet read, messages. It’s positioned in the navigation area.

    UnreadMessagesCounter-这是我之前提到的组件。 它显示一个计数器,指示已接收但尚未读取的消息的数量。 它位于导航区域中。

  • UserProfile — this the component responsible for rendering an input field the user can use for changing its user name. It’s saving the new user name into the local storage, using a debounce function. This means that the actual triggering of the function is happening some defined time after the user stops typing. It also triggers another Redux action, so we can use the new username in our Redux state.

    UserProfile-该组件负责呈现用户可用于更改其用户名的输入字段。 它将使用反跳功能将新用户名保存到本地存储中。 这意味着该函数的实际触发发生在用户停止键入后的某个定义的时间。 它还会触发另一个Redux动作,因此我们可以在Redux状态下使用新的用户名。

  • pages/ChatPage — parent component that encloses everything shown on the Chat page.

    pages / ChatPage-父组件,其中包含“聊天”页面上显示的所有内容。

  • pages/SettingsPage — parent component that encloses everything shown on the Settings page.

    pages / SettingsPage —父组件,其中包含“设置”页面上显示的所有内容。

Everything described above was related to our React components. All of them are responsible for getting some data and displaying it in a proper way. In order to be able to handle this data in a convenient for us way, we use a few more things. Let’s have a look at these things in the sections below.

上述所有内容都与我们的React组件有关。 他们所有人都有责任获取一些数据并以适当的方式显示它们。 为了能够以一种方便的方式处理这些数据,我们使用了其他一些方法。 让我们在下面的部分中看看这些东西。

Redux状态管理 (Redux State Management)

Here we will talk about how our app state is being managed by using Redux and socket middleware.

在这里,我们将讨论如何通过使用Redux和套接字中间件来管理我们的应用程序状态。

商店 (Store)

Our store is going to be relatively simple. We will have only two reducers defining a piece of the state reserved for the socket state and for the messages state. This is also where we apply our middleware. If you are familiar with Redux Saga package, you have probably seen this pattern of applying custom middleware when using Redux.

我们的商店将相对简单。 我们将只有两个化简器来定义为套接字状态和消息状态保留的一部分状态。 这也是我们应用中间件的地方。 如果您熟悉Redux Saga软件包,则可能已经看到使用Redux时应用自定义中间件的这种模式。

Something like this:

像这样:

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

But in our case it would be like this:

但是在我们的情况下,它将是这样的:

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import socketReducer from './socket/reducer';
import messageReducer from './message/reducer';
import socketMiddleware from './socket/middleware';
const rootReducer = combineReducers({
  socketState: socketReducer,
  messageState: messageReducer
});
// @ts-ignore
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const index = {
  ...createStore(rootReducer, composeEnhancers(applyMiddleware(socketMiddleware)))
};
export default index;

信息 (Message)

After defining our store, it’s time to see how are we going to handle the messaging in Redux. We have defined our actions here and our messageReducer here.

定义了商店之后,该看一下我们将如何处理Redux中的消息传递了。 我们已经定义了我们的行动在这里和我们messageReducer 这里 。

  • Actions — here we define the main actions needed for sending and receiving messages, as well as changing the user name.

    动作 -在这里,我们定义了发送和接收消息以及更改用户名所需的主要动​​作。

  • Reducer — here is where our messageReducer function lives and where we define what happens when one of the actions above is dispatched.

    减速器 -这是我们的messageReducer函数的生存地,我们在其中定义了调度以上操作之一时将发生的情况。

插座 (Socket)

We follow the same logic as above here. We have our socket actions, the middleware I mentioned above, and the socketReducer.

我们遵循与上述相同的逻辑。 我们有套接字操作 ,我上面提到的中间件和socketReducer 。

  • Actions — contains actions for connecting the socket (the one dispatched from the Navigation component in the beginning when the application is started) and one for when the connection status is changed, i.e. showing if we are connected or not.

    操作 -包含用于连接套接字的操作(在应用程序启动时从头开始从Navigation组件调度的操作),以及用于在连接状态更改时进行的操作,即显示是否已连接。

  • Middleware — contains implementation of a simple socket middleware, which provides us with the minimum functionality we need in our chat app.

    中间件 -包含一个简单的套接字中间件的实现,该中间件为我们提供了聊天应用程序中所需的最低功能。

  • Reducer — here is where our socketReducer function lives and where we define what happens when one of the actions above is dispatched.

    减速器 -这是我们的socketReducer函数所在的位置,也是我们定义在调度上述操作之一时发生的情况的位置。

主题化 (Theming)

In order to implement the possibility for setting different color themes in our application and considering the fact we are using styled-components, I used a ThemeProvider — component provided by them. Here is the implementation that includes defining objects with custom colors used in the themes.

为了实现在我们的应用程序中设置不同颜色主题的可能性,并考虑到我们正在使用样式化组件的事实,我使用了它们提供的ThemeProvider组件。 这是包括使用主题中使用的自定义颜色定义对象的实现。

The logic behind applying the selected color theme resides here. Ideally the containing component should be named something different than TranslationsProvider, as it doesn’t handle only the translations, as we see. We could add this to the list of future improvements/refactoring.

应用所选颜色主题的逻辑就在这里 。 理想情况下,包含组件的名称应与TranslationsProvider不同因为正如我们所见它不能仅处理翻译。 我们可以将其添加到将来的改进/重构列表中。

Here is how the existing color themes look:

以下是现有颜色主题的外观:

实用工具 (Utilities)

In almost every software project, at certain point, the need of common reusable functions emerges. This the moment when developers usually create a common shared file or files, containing such helpers functions. In our case this would be /utilities folder that currently contains four files. I will go through each of them below and explain the logic behind my decision to create it and put it there:

在几乎每个软件项目中,都在某些时候出现了对通用可重用功能的需求。 这是开发人员通常创建一个或多个包含此类帮助程序功能的文件的时刻。 在我们的情况下,这将是/ utilities 当前包含四个文件的文件夹。 我将在下面逐一介绍它们,并解释创建和放置它的决策背后的逻辑:

  • common.ts — here is the place where I decide to put such common helper functions, which are supposed to be easily used where needed in the whole application. In this specific case you will find four functions used for time formatting, and a helper for defining the active page and for scrolling an element to bottom.

    common.ts —在这里,我决定放置这些通用的辅助函数,这些函数应该容易在整个应用程序中使用。 在这种特定情况下,您将找到四个用于时间格式设置的功能,以及一个用于定义活动页面并将元素滚动到底部的助手。

  • localStorageService.ts — I have already mention this service in the first part of this tutorial. Here is where all methods for manipulating the local storage live.

    localStorageService.ts —我已经在本教程的第一部分中提到了此服务。 这是操作本地存储的所有方法的所在地。

  • TranslationsProvider.tsx — this component was also mentioned multiple times, but for the sake of clarity I will mention it again here. It contains the logic for providing translations and color theme in the app.

    TranslationsProvider.tsx —也多次提到了此组件,但为清楚起见,我将在这里再次提及。 它包含在应用程序中提供翻译和颜色主题的逻辑。

  • withTranslations.tsx — this is a higher-order component (HOC) which is responsible for attaching the application context (containing the translations and themes themselves) to any component being wrapped by it.

    withTranslations.tsx —这是一个高阶组件(HOC) ,负责将应用程序上下文(包含翻译和主题本身)附加到由它包装的任何组件。

Here is an example of how is it used:

这是一个用法示例:

export default withTranslations(SettingsPage as React.FunctionComponent);

We have walked a long way to here and we still haven’t started with the actual implementation.

我们到这里已经走了很长一段路,但我们还没有开始实际的实现。

That is a vivid pointer for us to show how important and extensive the planning phase of a project could be.

这是向我们表明项目计划阶段可能多么重要和广泛的生动指示。

Let’s jump now to the implementation phase in the next section.

现在让我们跳到下一部分的实现阶段。

实作 (Implementation)

If you reached this point of the tutorial, you should have a very clear idea of what are we going to build. Here, we are about to find out how are we going to do it.

如果您达到了教程的这一步,您应该对我们将要构建的内容有一个非常清晰的认识。 在这里,我们将了解我们将如何做。

从小开始 (Starting small)

As with any other project we should strive to start with small, incremental chunks and build on them. In our case I have decided to start first with building the header navigation. The reason for that was that I wanted to have the router and the navigation controls in place, so I could easily navigate through the tabs while developing and testing.

与任何其他项目一样,我们应该努力从小的增量块开始,并在它们之上进行构建。 在我们的情况下,我决定首先开始构建标题导航。 这样做的原因是我想安装路由器和导航控件,以便在开发和测试时可以轻松浏览选项卡。

设定页面 (Settings page)

After I had finished with the header and navigation parts, I decided to jump to the settings page first. Again, my reasoning was very simple — I wanted to build first what I was going to use in the Chat page. In other words I wanted to be able to customize my chat area, messages, ways of sending and so on, before implementing them.

在完成标题和导航部分之后,我决定首先跳到设置页面。 同样,我的推理非常简单-我想首先构建要在“聊天”页面中使用的内容。 换句话说,我希望能够在实现之前自定义我的聊天区域,消息,发送方式等。

So I started building component by component as I described them in the previous section. Once I had the full Settings page finished, I was able to go and start implementing the Chat page components. But before that, I had to take care of the supporting stuff — integrating with local storage and adding translations mechanism.

因此,如上一节所述,我开始逐个组件地进行构建。 完成完整的“设置”页面后,便可以开始实施“聊天”页面组件。 但是在此之前,我必须照顾好支持的工作-与本地存储集成并添加翻译机制 。

聊天页面 (Chat page)

After I have done all from above, the implementation of the Chat page and its components was fairly easy. I had to take care of the visual part manly and make the integration with the Redux store. As you already saw, I had to implement only two components which are shown and used on the Chat page — ChatArea and MessageSender.

从上面完成所有操作后,“聊天”页面及其组件的实现就相当容易了。 我必须亲自照顾视觉部分,并与Redux商店进行整合。 如您所见 ,我只需要实现在聊天页面上显示和使用的两个组件-ChatArea和MessageSender。

增加改进 (Adding improvements)

I want to say a few words here regarding the app improvements we did or will do in the future. Usually when we have a new requirement (let’s call it “requirement”, that makes is sound closer to what would be in a real project), it is again a very good idea to do some initial research, instead of jumping directly into implementation. You will be surprised how many solutions are already out there, waiting for us to use them.

我想在这里说几句关于我们已经或将来会做的应用程序改进。 通常,当我们有一个新需求时(我们称其为“需求”,这听起来更接近真实项目中的需求),再次进行一些初步研究,而不是直接跳入实施也是一个很好的主意。 您会惊讶于已经有多少解决方案在等待我们使用。

In other words, we don’t have to invent the wheel again.

换句话说,我们不必再次发明轮子。

This is what I did when I started thinking about adding support for emoticons or link parsing. It turned out that there are already solutions I could use with a little tweaking from my side, just to make them fit well in my project.

这就是我开始考虑添加对表情符号或链接解析的支持时所做的。 事实证明,我已经可以通过一些调整来使用解决方案,只是使它们很好地适合我的项目。

Here are the links to the packages I used:

这是我使用的软件包的链接:

  • https://www.npmjs.com/package/linkifyjs

    https://www.npmjs.com/package/linkifyjs

  • https://docs.microlink.io/sdk/getting-started/react/

    https://docs.microlink.io/sdk/getting-started/react/

  • https://www.npmjs.com/package/react-emojione

    https://www.npmjs.com/package/react-emojione

  • https://www.npmjs.com/package/get-urls

    https://www.npmjs.com/package/get-urls

And here you can see how I used them in our chat app.

在这里您可以看到我如何在聊天应用程序中使用它们。

部署到Heroku (Deploying to Heroku)

I have written another article in the past. It was about totally different subject, but there is a part exactly related to how to deploy an app to Heroku. You might find it useful to check it out.

我过去写过另一篇文章 。 这是一个完全不同的主题,但是有一部分与如何将应用程序部署到Heroku完全相关。 您可能会发现将其签出很有用。

For deploying our chat application to Heroku, I will assume you have already an account and can easily follow the steps below:

要将您的聊天应用程序部署到Heroku ,我假设您已经有一个帐户,可以轻松地执行以下步骤:

  1. npm build to build the project to build folder.

    npm build来建立项目来build文件夹。

  2. Add build folder to Git to make sure it will be committed.

    build文件夹添加到Git以确保将其提交。

  3. Make sure that express server loads static resources from it.

    确保快速服务器从中加载静态资源。
  4. Commit all: git commit -m 'Deploy to Heroky'.

    全部提交: git commit -m 'Deploy to Heroky'

  5. Run git push heroku master.

    运行git push heroku master

  6. Open the app from the given URL (in my case: mihails-chat.herokuapp.com).

    从给定的URL(在我的情况下: mihails-chat.herokuapp.com )中打开应用程序。

未来(可能的)计划 (Future (possible) plans)

At the time of writing this I was thinking it might be very interesting to try building the same application with the other super famous UI library on the market — Angular. I still think it will be worth it, but I am not sure whether I will have the time and the power to do it ?.

在撰写本文时,我认为尝试与市场上另一个超级著名的UI库Angular构建相同的应用程序可能非常有趣。 我仍然认为这是值得的,但是我不确定我是否有时间和力量去做?

In any case, what I am thinking about it as a pure, technical comparison of two major UI libraries from the developer’s point of view.

无论如何,从开发人员的角度来看,我将其视为两个主要UI库的纯技术比较。

If I do it, I will make sure you know it!

如果我这样做,我将确保您知道!

Thanks for reading. You can read more of my articles at mihail-gaberov.eu.

谢谢阅读。 您可以在mihail-gaberov.eu上阅读我的更多文章。

翻译自: https://www.freecodecamp.org/news/build-a-chat-app-with-react-typescript-and-socket-io-d7e1192d288/

你可能感兴趣的:(大数据,python,人工智能,java,编程语言)