В предыдущих постах — часть 2 — я рассказывал о создании приложения React с бэкендом node.js с использованием фреймворка hapi.js.

В этом посте я опишу, как заставить нашего клиента общаться с нашим сервером, как его протестировать и настроить для разработки.

Цель такова:

Клиент реагирует, отображая имя, версию и статус серверной части.

Основой для этого поста является код с тегом «blog-post-2»:



Расширение серверной части

Работа API довольно проста, несколько новых методов и хранилище данных в памяти — myDataStore (его следует использовать только для тестирования).

Вызов работающего API (yarn start) должен возвращать имя и версию:

$ curl localhost:8080/api/version
Hello from hapi-with-react-socketio tutorial version 1.0.1

Я также добавил несколько других вещей в бэкэнд-проект:

  • автоматическое форматирование кода с использованием prettier и lint-staged
  • изменен текст метаданных проекта на руководство по hapi-with-react-socketio
  • добавлен набор тестов для новых методов API

Примечание о лучших практиках

В коде тестов есть одна строка, которая выглядит немного не так:

const DataStore = require('../../../server/api/index').data;

Поскольку API имеет состояние, тесты должны устанавливать и очищать это состояние во время выполнения. Это не то, что вы должны делать в рабочем коде. Это работает только здесь, потому что приложение работает в одном потоке, а зависимости (требования) кэшируются, поэтому и тесты, и сервер используют один и тот же экземпляр myDataStore. Если бы это был производственный код, я бы использовал какую-то инъекцию зависимостей и имитацию для управления этим состоянием (возможно, перемещенный в кэш в памяти, такой как Redis, или БД, например PostgreSQL).

Создание реактивного компонента

Чтобы отобразить информацию о версии, мы создадим компонент VersionInfo. Он попадет в наш API в componentDidMount и сохранит ответ в состоянии компонентов.

Это выглядит и работает отлично, но не очень хорошо проверяется. Тесты выявили одну проблему с этим кодом — что происходит, когда вызов API выполняется после размонтирования компонента (пользователь переходит на другую подстраницу)?

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

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

Я последовал совету Facebook по поводу паттерна isMounted.



Есть несколько интересных реализаций «отменяемых промисов»:

Я создал что-то маленькое и простое:

Отменяемый код выглядит так:

Тестирование реактивных компонентов

Тестировать компоненты, использующие fetch, легко благодаря библиотеке fetch-mock.

Запуск клиента и сервера

На самом деле существует несколько способов запуска клиентского и серверного кода.

Во-первых, это производственный способ, который состоит из следующих шагов:

  1. Соберите клиент — yarn build, который выполняет сборку всего проекта — yarn && cd client && yarn && yarn build
  2. Запустите сервер — yarn startкоторый обслуживает клиентские файлы в http://localhost:8080/app/

Это работает, но довольно медленно и раздражает вносить любые изменения в клиентский код, альтернативой является запуск клиента и сервера по отдельности. Это требует небольшого изменения в конфигурации, чтобы сообщить реагирующему клиенту, какой URL-адрес сервера. В client\package.json нам нужно добавить параметр proxy:

"proxy": "http://localhost:8080"

Далее нам нужно выполнить 2 команды в 2 окнах терминала

  1. Запустите сервер, выполнив сервер, используя yarn start в корневом каталоге
  2. Запустите клиент, выполнив yarn start в каталогеclient, он должен открыть http://localhost:3000/

Состояние API

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

Исходный код



Следующий

В следующем посте я хочу рассказать о socket.io.