Поддерживать полное состояние приложения React в бэкэнде? - PullRequest
1 голос
/ 13 апреля 2020

Я создаю в режиме реального времени веб-приложение типа «лобби», в котором размещается несколько пользователей (по 2–8 одновременно), где состояние лобби распределяется между пользователями. Пользовательский интерфейс построен с React. Каждый пользователь устанавливает соединение веб-сокета с бэкэндом после присоединения к лобби. В настоящее время они получают полное глобальное состояние приложения в виде объекта JSON (его размер не должен превышать несколько килобайт).

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

Лобби предоставляет пользователям ряд конечных пулов ресурсов, доступ к которым имеют все. Пользователи будут перемещать эти ресурсы между собой, а также в пулы и из них. В настоящее время я думаю, что полное состояние лобби и всех его пулов ресурсов хранится и поддерживается исключительно в бэкэнде. Когда пользователь хочет переместить ресурс, например, из пула в себя или наоборот, или изменить видимое состояние ресурса, это делается с помощью сообщений JSON, отправляемых через соответствующие соединения веб-сокетов.

Каждый действие, которое они выполняют, приводит к тому, что подобное сообщение отправляется через сокет (упрощенно):

{
  "action": "MOVE",
  "source": "POOL1",
  "target": "user_id_here",
  ...metadata...
}

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

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

Пока все хорошо. Учитывая типы действий, типы пулов ресурсов, количество пользователей и размер ожидаемых объектов состояния, частота обновлений не должна стать проблемой ни с точки зрения использования ресурсов, ни с точки зрения использования полосы пропускания.

Чтобы уточнить: нет действий, которые пользователи выполняют в пользовательском интерфейсе React, каким-либо образом изменяют свое локальное состояние. Каждое действие, которое они выполняют, преобразуется в сообщение JSON, как в примере выше, и результатом этого действия будет получение обновленного полного состояния приложения, которое полностью заменяет предыдущее состояние, которое React использовал для визуализации интерфейса пользователя. , Состояние приложения уровня React является эфемерным, используется только для его рендеринга один раз. Все визуализации происходят исключительно в ответ на обновления состояния через веб-сокеты.

Единственная область, с которой у меня возникают трудности, - это как структурировать это эфемерное состояние на стороне React, чтобы рендеринг обновленного объекта состояния был настолько быстрым и эффективный, насколько это возможно. Я бэкэнд и у меня нет опыта создания приложений React такого типа (в последний раз я использовал его четыре года go по-особенному c, передавая реквизиты глубоко вложенным дочерним компонентам, с состояние хранится повсюду). Я не совсем уверен, какие средства и инструменты использовать.

Например, я мог бы использовать провайдер контекста верхнего уровня с хуком useReducer, который многие называют «заменой Redux» (что технически не таково). «т). Или я мог бы использовать Redux, но действительно ли он добавляет какую-либо ценность в этом случае? Или что-то еще?

Учитывая, что все состояние заменяется в результате каждого действия каждого пользователя, каков наилучший, наиболее эффективный и наименее требующий времени способ структурирования стороны React?

1 Ответ

1 голос
/ 18 апреля 2020

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

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

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

  • Чтобы решить эту проблему, вы можете сохранить идентификатор транзакции для каждого пользователя, чтобы пользователь отправлял все сообщения данных с идентификатором транзакции до текущего состояния сервером, а затем приложение могло обрабатывать и обновите транзакции

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

Насколько что касается использования useReducer или Redux, вы должны решить, в зависимости от сложности вашего приложения.

Случаи, когда приложение небольшого размера, могут быть легко покрыты с помощью useReducer и useContext, но если ваши состояния сложны и вам нужно поддерживать несколько редукторов, вы должны go опередить Redux, поскольку он обеспечивает большую гибкость при хранении данных

РЕДАКТИРОВАТЬ:

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

Как только вы это сделаете, вы можете использовать параметр shouldComponentUpdate или areEqual для компонента класса или функционального компонента соответственно.

Идея здесь состоит в том, чтобы сравнить предыдущее и текущее значение, полученное из реквизита. и пусть go впереди с логикой рендеринга c или нет.

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

Также, когда вы используете connect для своего компонента приложения React, он фактически является функциональным компонентом, поэтому, если mapStateToProps не возвращает значение, чьи ссылки меняются, будет препятствовать повторному рендерингу, так как это PureComponent

. Я бы настоятельно рекомендовал вам go в документации shouldComponentUpdate, React.memo и redux. Также загляните в переберите библиотеку, которая поможет вам реализовать запомненные селекторы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...