[Из песочницы] Быстрый курс Redux + websockets для бэкендера

Это краткое руководство и обучение по фронтэнеду для бэкендера. В данном руководстве я решаю проблему быстрого построения пользовательского интерфейса к серверному приложению в виде одностраничного веб-приложения (single page app).


Основной целью моего исследования является возможность за разумное время (для одного нормального человека) получить удобный и простой в использовании интерфейс-черновик к серверному приложению. Мы (как разработчики серверной части) понимаем, что наш приоритет — серверная часть. Когда (в гипотетическом проекте) появятся во фронте профи своего дела, они все сделают красиво и «правильно».


В роли учебной задачи представлена страничка чата с каким-то умозрительным «ботом», который работает на стороне сервера и принимает сообщение только через WebSocket. Бот при этом выполняет эхо ваших сообщений (мы тут не рассматриваем серверную часть вообще).


Мне для изложения материала требуется, чтобы вы имели:


  • базовое знание javascript (тут нужно поискать в интернете справочник по крайней версии js стандартов ES-2015)
  • знание reactjs (на уровне обучения https://facebook.github.io/react/tutorial/tutorial.html)
  • понятие о websockets (это очень просто, главное чтобы ваш сервер это умел)
  • знание и умение использовать bootstrap (на уровне этого раздела http://getbootstrap.com/css/)

Что используем


Redux — официальная документация расположена по адресу http://redux.js.org. По-русски есть несколько вариантов, я лично использовал в основном https://rajdee.gitbooks.io/redux-in-russian/content/docs/introduction/index.html.


Статью exec64, она стала причиной написать этот тутриал https://exec64.co.uk/blog/websockets_with_redux/.


Готовый сервер с react и redux от https://github.com/erikras/react-redux-universal-hot-example (он нам спасает человеко-месяцы времени по настройке большой связки технологий, которые необходимы для современного js проекта)


Мотивация


Вообще я разрабатываю приложение на языке Python. Погоди-погоди уходить …


Что мне было нужно:


  • мне нужно чтобы реализация интерфейса не диктовала мне выбор технологий на стороне серверной части
  • современные технологии (мне нечего было терять или быть чем-то обязанным «старым проверенным приемам»)
  • это должно быть одностраничное приложений (я уже сам выберу, где можно обновлять страницу целиком)
  • мне нужна реакция пользовательского интерфейса в реальном времени на серверные события
  • мне нужен обмен информацией сервер-клиент (а не клиент-сервер) в реальном времени
  • мне нужна возможность генерировать обновления клиента на сервере

Что было испробовано:


  • вариации на тему на чистом js (устарело, есть много полезных моделей велосипеда)
  • JQuery (уже не могу ТАК извратить так свой мозг, крайне сложный для быстрого старта синтаксис и… это дело профессионалов)
  • Angular (переход на 2 версию спугнул и не нашел за отведенное время лазейки к решению моей задачи)
  • Socket.io (там все реализовано, если вы node.js программист вы уже его используете, но он слишком сильно привязывает серверную часть на node, мне нужен только клиент без третьих лиц)

Выбрано в итоге:


  • React (понятно и доступно/просто + babel = делает язык вполне понятным)
  • Redux (импонирует использование единой помойки единого хранилища)
  • WebSockets (очень просто и не связывает руки, а позволяет внутри себя уже применять такой формат какой позволит фантазия)

Упрощения и допущения:


  • Мы не будем использовать авторизации в приложении
  • Мы не будет использовать авторизации в WebSocket-ах
  • Мы будем использовать самое доступное приложение Websocket Echo (https://www.websocket.org/echo.html)

Содержание


  • Часть первая. Первоначальная настройка. Настройка одной страницы
  • Часть вторая. Проектирование будущего приложения
  • Часть третья. Изумительный Redux
  • Часть четвертая. Оживляем историю
  • Часть пятая. Проектируем чат
  • Часть шестая. Мидлваре

Как читать

Не будете повторять — пропускайте часть 1
Знаете reactjs — пропускайте часть 2
Знаете redux — пропускайте части 3, 4 и 5
Знаете как работает middleware в redux — смело читайте часть 6 и далее в обратном порядке.


Часть первая. Первоначальная настройка. Настройка страницы.

Настройка окружения


Нам нужен node.js и npm.


Ставим node.js с сайта https://nodejs.org —, а именно этот гайд написан на 6ой версии, версию 7 тестировал — все работает.
npm устанавливается вместе с node.js


Далее нужно запустить npm и обновить node.js (для windows все тоже самое без npm)


sudo npm cache clean -f
sudo npm install -g n
sudo n stable

проверяем


node -v

Настройка react-redux-universal-hot-example


Все выложено в react-redux-universal-hot-example, там же инструкция по установке.
Тут привожу последовательность действий


  1. Скачиваем и разархивируем архив/форкаем/что-угодно-как-вам-нравится.
  2. Через node.js command line или терминал переходим в эту папку
  3. Запускаем

npm install
npm run dev

Переходим на http://localhost:3000 и должны видеть стартовую страницу.


Если все ок — приступаем.


Создаем новый контейнер


Для настройки раздела используем предоставленную справку от команды react-redux-universal-hot-example. Оригинал статьи находится тут.


cd ./src/containers && mkdir ./SocketExample

Копируем туда hello.js как шаблон странички


cp About/About.js Hello/SocketExamplePage.js

Я использую для всего этого Atom, как действительно прекрасный редактор-чего-угодно с некоторыми плюшками.


Правим скопированный файл


Создаем заглушку под нашу страница. Вводим элемент

. Позже будем выводить статус соединения в этот элемент.


import React, {Component} from 'react';
import Helmet from 'react-helmet';

export default class SocketExamplePage extends Component {
  render() {
    return (
      

Socket Exapmle Page

Sockets not connected

); } }

Подключаем созданную страницу


Добавляем в ./src/containers/index.js новый компонент React


export SocketExamplePage from './SocketExample/SocketExamplePage';

Добавляем в ./src/routes.js, чтобы связать переход по пунти /socketexamplepage в карту ссылок


...
import {
    App,
    Chat,
    Home,
    Widgets,
    About,
    Login,
    LoginSuccess,
    Survey,
    NotFound,
    SocketExamplePage
  } from 'containers';
...
      { /* Routes */ }
      
      
      
      
      
...

Добавляем в ./src/containers/App/App.js, чтобы добавить пункт в меню


              
                Socket Example Page
              

Проверяем


npm run dev

Коммит: https://github.com/valentinmk/react-redux-universal-hot-example/commit/69935996671fc5dd64062143526d1a00b49afcbd


На данный момент мы имеем:


  • Раздел веб приложения
  • Страничка на React для нашего приложения
  • Заготовка, чтобы идти дальше

Прежде чем начнем. Я все разрабатывал в обратном порядке — сначала крутил мидлваре, потом прокидывал экшены и только потом уже прикручивал адекватный интерфейс в reactjs. Мы в руководстве будем делать все в правильном порядке, потому что так действительно быстрее и проще. Минус моего подхода в том, что я использовал в разы больше отладки и «костылей», чем нужно на самом деле. Будем рациональными.

Часть вторая. Проектирование будущего приложения

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


В руководстве для начинающих React представлен подход по проектированию динамических приложений на React, от которого мы не будем отклоняться, а прямо будем следовать по нему.


Дэн Абрамов писал в своей документации много про то, что и как нужно разделять в приложении и как организовывать структуру приложения. Мы будем следовать его примеру.


Итак начнем.


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


Пользовательский интерфейс «Вариант 1»


Мы добавляем два новых раздела на нашу страницу.


В логе подключения сокетов будем кратко выводить текущие события, связанные с подключением отключением. Изменяем файл ./src/containers/SocketExample/SocketExamplePage.js.


// inside render () { return (...) }
          

Socket connection log