Моей реальности нужно воображение, как лампочке нужна розетка. Моему воображению нужна реальность, как слепому трость.
Том Уэйтс

На этой неделе мы собираемся добавить базовую связь между нашими клиентами через веб-сокеты.

Что такое веб-сокеты, спросите вы? Это протокол, обеспечивающий двусторонний, всегда включенный канал связи между клиентом и сервером.

HTTP, который является базовым протоколом для запросов REST (см. предыдущую статью), не имеет состояния и работает по принципу запустил-забыл. Вы делаете запрос, сервер отвечает, и все.

Однако иногда мы хотим оставить «линию открытой» между нашим сервером и нашим клиентом, чтобы обеспечить связь в реальном времени: введите Websockets.

Что мы собираемся сделать сегодня, так это добавить очень простое взаимодействие на стороне клиента, позволив пользователю выбирать карты. И мы будем синхронизировать в режиме реального времени этот выбор для всех клиентов.

Это, конечно, только базовый пример, где все «видят» одно и то же, но это шаг к нашей реализации игры в покер с планированием.

Это как одноранговый? Не совсем так: общение всегда происходит между клиентом и сервером, а не напрямую между клиентом и клиентом. Чтобы включить связь клиент-клиент, вы фактически отправляете свое сообщение на сервер, который затем отправляет его обратно другому клиенту (клиентам).

Добавление некоторого взаимодействия на стороне клиента (коммит)

В этом коммите мы собираемся немного изменить наше приложение, чтобы добавить новую функцию: возможность выбирать карты из колоды (которую мы получаем с сервера, см. предыдущую статью) и добавлять их в выборку.

Во-первых, нам нужно внести несколько изменений в наш компонент Card: если вы посмотрите на файл Card/index.js в нашем коммите, вы увидите, что мы добавили две вещи:

  • Обработчик onClick, позволяющий карточке реагировать на клики.
  • Изменение CSS, делающее курсор мыши похожим на указатель в виде руки, чтобы сообщить пользователю, что карта активна.

Во-вторых, нам нужно внести некоторые коррективы в макет, создав несколько новых разделов (строки 16, 21 и 25) в App.js. Они будут контейнерами для нашей страницы, нашей колоды и нашего выбора соответственно.

В строке 31 видно, что мы добавили новый массив в наше состояние: selection . Это массив, в который мы собираемся добавлять карты каждый раз, когда мы нажимаем на карту в колоде.

Затем в строке 39 мы добавляем метод для обработки добавления карты к выборке: мы устанавливаем новый массив выбора для состояния, используя предыдущий массив и добавляя карту в качестве последнего элемента.

Почему мы не изменили существующий массив, выполнив selection.push(card)? Поскольку React хочет, чтобы состояние и свойства были неизменяемыми, поэтому, когда мы делаем [ ...oldArray, newItem ], мы фактически создаем новый массив.

И последнее, но не менее важное: мы объединяем все это в методе рендеринга, отображая карты как для колоды, так и для нашего выбора.

Вуаля!

Представляем Socket.io (коммит)

Во-первых, давайте установим несколько новых зависимостей:

yarn add socket.io socket.io-client

Поскольку мы добавляем некоторое взаимодействие клиент-сервер, нам нужно изменить оба.

"Сервер"

Настроить сервер довольно просто: вы подключаете Socket.io к существующему приложению Express в строке 7.

Затем вы используете io.on('connection', ...) для обработки каждого клиента, подключающегося к вашему серверу через веб-сокеты. Каждый новый клиент будет отображать сообщение на консоли вашего сервера и получать текущий выбор, выполнив socket.emit() (строка 34).

Вам также необходимо обрабатывать любые обновления этого выбора, прослушивая сообщение SEND_SELECTION в строке 37. Каждый раз, когда это сообщение отправляется одним из клиентов, сервер будет отвечать, передавая этот выбор всем остальным (строка 40).

Прежде чем мы посмотрим, что происходит на стороне клиента, вам также необходимо изменить файл package.json, чтобы добавить новое прокси-правило для конечной точки socket.io (строка 31). Без этого клиент не найдет конечную точку socket.io, когда вы работаете на своей локальной машине.

"Клиент"

На клиенте процесс аналогичен: мы создаем экземпляр нового соединения Websocket, вызывая функцию io() в строке 40.

Мы прослушиваем сообщения RECEIVE_SELECTION и обновляем наш локальный выбор каждый раз, когда сервер отправляет новое сообщение (строка 49).

Если это наш клиент делает изменение в выборе (нажав на одну из карточек), нам нужно уведомить сервер: в методе selectCard мы ждем, пока не установится новое состояние (поэтому мы подключаемся к обратному вызову функции setState ), чтобы отправить сообщение на сервер об отправке нового выбора (используя сообщение aSEND_SELECTION с новым выбором в качестве полезной нагрузки).

Результат

После того, как вы проверили код, запустите сервер и откройте два (или более) браузера:

Вы увидите, что всякий раз, когда вы добавляете карту в выборку, все браузеры обновляют выборку одновременно: они синхронизируются!

Если вы убьете сервер, вы также заметите, что все выборки очищаются, а любые новые выборки не синхронизируются.