Введение
Мы создадим простое чат-приложение на ReactJS на основе уже реализованного нами сервера. Начнём с создания статических компонентов, которые затем подключаются к состоянию. На последнем этапе мы подписываемся на сервер событий.
Предпосылки
- Базовые знания JavaScript и ReactJS (я стараюсь излагать всё просто)
- npm v6
- npx v6
- узел v12
Шаг 1: Запуск проекта
В папке проекта выполните команду npx create-react-app --use-npm simple-chat-app и дождитесь завершения установки. Это добавит все необходимые зависимости и упростит процесс разработки и сборки.
Добавьте следующие файлы в папку src:
- App.js (должен уже существовать)
- ChatLineHolder.js
- ChatLine.js
- ChatInput.js
Выполните команду npm run start из папки проекта в консоли. Это запустит сервер разработки и откроет приложение в браузере по умолчанию.
В папке src уже должен быть файл App.js. Пока что вы можете удалить всё, что он возвращает, и оставить только один. <div> Верните его.
const App = () => {
return <div className='chat-app'></div>
}Мы начинаем с создания наших компонентов снизу вверх.
Шаг 2: Компонент
В ChatLine.js мы создаем новый компонент, имеющий два свойства:
имя: нитьсообщение: нить
Это данные, которые мы позже получим с сервера как данные о событиях.
Компонент первый
import React from 'react'
const ChatLine = ({ name, message }) => {
return (
<li>
{name}: {message}
</li>
)
}
export defaultПримечание: ReactJS автоматически предотвращает внедрение HTML-кода, так что вам не о чем беспокоиться.
Шаг 3: Компонент
В ChatHolder.js мы создаем новый компонент, который принимает только одну базу:
Строки: Массив<{ имя: строка, сообщение: строка }>
Если линии Если пусто, мы отображаем простое уведомление.
if (lines.length === 0) {
return <div>Chat history is empty</div>
}Lines — это массив объектов. Каждый объект имеет имя и сообщение о свойстве. Мы сопоставляем все элементы массива, чтобы получить результат, передавая имя и строку компоненту. Создайте несколько узлов ChatLine.
const chatLines = lines.map(
(line, index) => (
<ChatLine key={index} name={line.name} message={line.message} />
)
)Тогда результат в
- Выполняем рендеринг и получаем готовый компонент:
- onSend: (сообщение: строка) => void
- Имя: Имя пользователя, который общается в чате.
- chatLines: Массив, представляющий историю чата.
- eventLister: используется для управления событиями с сервера. (Примечание: я решил поместить прослушиватель в часть.
- Отправить сообщение на сервер
- Подписаться на события, отправленные сервером
import React from 'react'
import ChatLine from './ChatLine'
const ChatLineHolder = ({ lines }) => {
if (lines.length === 0) {
return <div>Chat history is empty</div>
}
const chatLines = lines.map(
(line, index) => (
<ChatLine key={index} message={line.message} name={line.name} />
)
)
return (
<ul>
{chatLines}
</ul>
)
}
export default ChatLineHolderПримечание: Обычно в качестве ключа используется уникальный идентификатор. В данном случае можно смело использовать индекс, поскольку связь между индексом и объектом не меняется. В реальном приложении это может привести к серьёзным проблемам.
Шаг 4: Компонент
Как вы могли заметить, возможности импорта сообщений по-прежнему нет. Обрабатывает ввод текста и использует функцию обратного вызова при отправке для создания нового сообщения чата при нажатии кнопки. Требуется свойство:
Компонент пользовательского ввода сохраняет свое состояние и управляет обновлениями.
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}При нажатии кнопки «Отправить» вызывается функция onSend с текущим состоянием. Поскольку мы не хотим, чтобы пользователю приходилось вручную очищать поле ввода, мы сбрасываем состояние после вызова onSend.
const onClickSend = () => {
onSend(value)
setValue('')
}
Вот последний компонент:
import React, { useState } from 'react'
const ChatInput = ({ onSend }) => {
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
const onClickSend = () => {
setValue('')
onSend(value)
}
return (
<div>
<input type='text' value={value} onChange={onChange} />
<button onClick={onClickSend}>send</button>
</div>
)
}
export default ChatInputШаг 5: Компонент
Режим
Поскольку наши компоненты не хранят никаких данных, В качестве основного компонента мы используем управление состояниями. У нас есть 3 типа состояний:
(Для простоты в реальном приложении вы можете использовать другой подход.)
Для инициализации нашего состояния мы используем хуки состояния:
const [chatLines, setChatLines] = useState([])
const [eventListener, setEventListener] = useState(null)
const [name] = useState(generateRandomName())Тест рендеринга компонентов
Сейчас мы просто отображаем наше имя и компонент. Мы представляем chatLines с фиксированными элементами. Поскольку chatLines — пустой массив, наше уведомление должно быть отправлено. Мы вызываем пустую функцию Flash, чтобы Мы отправим. Исполнение последует на следующем этапе.
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={() => {}} />
</div>
)Если вы хотите проверить, работает ли ваша реализация с некоторыми данными, просто добавьте несколько элементов в chatLines.
const [chatLines, setChatLines] = useState([{ name: 'Thomas', 'message': 'Hi' }])
Добавить взаимодействие
Мы уже можем вводить текст в поле ввода чата, но нажатие кнопки «Отправить» ничего не даёт. Это связано с тем, что у нас есть функция, которая называется Мы отправили его, который абсолютно ничего не делает. Поэтому нам нужна реализация.
Поскольку мы ещё не подключены к серверу, мы реализуем onSend таким образом, чтобы он напрямую обновлял состояние. Для этого добавим вспомогательную функцию addChatLine: (chatLine) => void. Мы будем использовать её позже для обработки сообщений, отправляемых сервером.
const addChatLine = (chatLine) => {
setChatLines(chatLines => {
return [...chatLines, chatLine]
})
// If you don't have a lot of experience with ReactJS, just think of this as:
// setChatLines([...chatLines, chatLine])
}onSend создает объект строки чата и вызывает addChatLine
const onSend = (message) => {
const chatLine = { name, message }
addChatLine(chatLine)
}Не забудьте отправить Отправлять.
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={onSend} />
</div>
)Теперь вы сможете ввести текст в поле ввода, нажать «Отправить» и мгновенно увидеть свое сообщение.
Шаг 6: Подключитесь к серверу
Общаться с самим собой очень скучно. Поэтому нам нужно подключить наше приложение к серверу. Это происходит в два этапа:
Отправить сообщение на сервер
Нам нужно изменить написанную нами функцию onSend. Мы можем заменить всё тело простым вызовом fetch, который отправляет наше имя и сообщение. Важно не вызывать addChatLine, так как мы сделаем это позже, когда получим событие, отправленное сервером.
const onSend = async (message) => {
fetch('http://localhost:4000/say',
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: 'post',
mode: 'no-cors',
body: `name=${name}&message=${message}`
})
}Если вы нажмёте кнопку «Отправить» сейчас, запрос будет отправлен на сервер, но ваш статус пока не обновится! Мы решим эту проблему на следующем этапе.
Подписаться на события, отправленные сервером
Прежде чем начать прослушивать события, нам нужна функция, обрабатывающая такие события, как сообщения. Поскольку позже мы будем подключаться к событиям типа «сообщения», мы можем реализовать её соответствующим образом. Всё, что делает функция, — это принимает event.data, преобразует его в объект и вызывает addChatLine.
const onMessage = (event) => {
const chatLine = JSON.parse(event.data)
addChatLine(chatLine)
}
Для получения событий, отправляемых сервером, мы создаём объект класса EventSource с нашим локальным адресом. Затем добавляем прослушиватель событий для этого типа сообщения. Затем оборачиваем его в хук useEffect (это гарантирует, что когда компонент Он монтируется, инициализируется источник событий, а затем закрывается после монтирования).
useEffect(() => {
let source = new EventSource('http://localhost:4000/listen')
source.addEventListener('message', onMessage)
setEventSource(source)
return () => { source.close() }
}, [])Компонент В конечном итоге это выглядит так:
import React, { useEffect, useState } from 'react'
import ChatLineHolder from './ChatLineHolder'
import ChatInput from './ChatInput'
const App = () => {
const [chatLines, setChatLines] = useState([])
const [eventSource, setEventSource] = useState(null)
const [name] = useState(generateRandomName())
const addChatLine = (chatLine) => {
setChatLines(chatLines => [...chatLines, chatLine])
}
const onMessage = (event) => {
const chatLine = JSON.parse(event.data)
addChatLine(chatLine)
}
useEffect(() => {
let source = new EventSource("http://localhost:4000/listen")
source.addEventListener('message', onMessage)
setEventSource(source)
return () => { source.close() }
}, [])
const onSend = (message) => {
fetch(
`http://localhost:4000/say`,
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "post",
mode: 'no-cors',
body: `name=${name}&message=${message}`,
})
}
return (
<div className='chat-app'>
<h2>Chatting as {name}</h2>
<ChatLineHolder lines={chatLines} />
<ChatInput onSend={onSend} />
</div>
)
}
export default AppРезультат
Теперь вы можете открыть свою страницу в двух вкладках и общаться с двумя альтер-эго! Вы также можете дважды смонтировать компонент App (изменяя index.js), поскольку у каждого экземпляра своё состояние.









