[Разработка веб-сайтов, ReactJS] ReactJs и бизнес логика в Актерах
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Где же держать бизнес логику приложения? "Та чего уж там париться - прямо в компонентах." - скажут некоторые. И в некоторых ситуациях это правильное и удобное решение.Но что если мы все-таки хотим чего-то большего? Например, масштабируемости, несколько юзер интерфейсов, лёгкой смены дизайна. Тогда логично разделить приложение на два слоя. Слой бизнес логики и слой представления. Как написано на сайте reactjs.org, React - это библиотека для создания UI. А для модели и бизнес логики написано несколько других замечательных библиотек.Так уж получилось, что проекты, на которых я работал, использовали redux. Поэтому дальше речь пойдет про то, как построить бизнес логику в большом react/redux приложении, и чтобы потом не закрывать рукой глаза при виде того огромного количества редюсеров, экшенов и так далее и тому подобное.СлоиЧтобы не запутаться в своем коде, я привык все делить на разные слои. Вы тоже это делаете, когда стили храните в одном файле, контейнер - в другом, компонент - в третьем, константы в четвертом, переводы в пятом, стейтлесс компоненты - в шестом, а ещё редюсеры, экшены, селекторы, Что? Не храните в разных файлах? Не, ну пусть даже вы назвали это duck подходом и запихнули все в один файл, вы все равно имеете в виду что он состоит из разных слоев.Деление на слои помогает ориентироваться в коде. Вы уже не просто смотрите 125-ю строчку кода вывода таблицы X, а слой контейнера таблицы X.Деление на слои помогает заменять отдельные слои. Например, у вас есть стейтлесс компонент. Пока редакс не готов, вы можете временно подключить к стейтфулл компоненту, а потом стейтфулл компонент заменить на контейнер подключенный к редаксу.Деление на слои помогает разделить работу между разработчиками.
Итак, деление на слои - это хорошо.
Что нам даёт отдельный слой бизнес логики?Как правило когда мы выводим пользователю какою-то страницу с десятком компонентов, то все эти компоненты не сами по себе, они взаимодействуют друг с другом ради одного общего дела. Многие делают ошибку - инкапсулируют логику в отдельные соответствующие компоненты.Например, представим страницу с таблицей и фильтрацией и Вы все-таки расположили логику фильтрации в компоненте FilterComponent.И вдруг понадобилось добавить панель с кнопкой (BottomPanel) сброса фильтра в другой части UI. Часто я вижу такое решение: Оставляют FilterComponent с его состоянием и добавляют еще BottomPanel в которой дублируется тоже состояние, и потом пытаются эти два состояния синхронизировать. И первое время все работает как положено. Но постепенно появляются другие связанные компоненты и контроль над ситуацией теряется. Из такого подхода вырастает очень не тривиальный и сложный в поддержке код.Правильнее будет вынести эту логику в корневой компонент, в котором все взаимодействующие компоненты рендерятся. Тогда поток данных будет предсказуем - пропсы от родителя к дочерним компонентам, события от дочернего компонента к родителю.
Размещать бизнес логику в корневом компоненте - это хорошо.
Слой бизнес логики без ReduxДо появления хуков общую логику компонентов выносили либо в HOC либо описывали прямо в корневом для этой логики компоненте . С появлением хуков все стало наглядней. Хуки отличный претендент для слоя бизнеса логики и переиспользования общей логики в разных компонентах. Они очень удобные и если бы не парочку нюансов, можно было бы ими ограничится.
Хуки для бизнес логики - это хорошо
ReduxПервое, чего не хватает при работе с хуками и то что даёт редакс - это общее состояние всего приложения.Общее состояние - это отдельный слой, а как мы выяснили в самом начале статьи - чем больше разных слоев, тем лучше.Редакс предоставляет инструменты для дебага общего состояния.Общее состояние позволяет передвигаться по истории, что открывает дополнительные возможности при тестировании. Можно сохранить состояние и потом восстанавливать для нескольких веток теста.Можно пролистать все виды интерфейса пользователя просто меняя состояние.
Общее состояние приложения в одном месте - это хорошо.
Thunk, Saga и АктерыТ.к. у нас redux, то часть логики приложения попала в редюсеры. Но с редюсерами одна проблема - они синхронные. Нельзя в редюсере сделать запрос на сервер и результат вернуть в редакс. Чтобы добавить асинхронности, разработали несколько расширений для редакса используя специальному механизм - middleware. Этот механизм позволяет выполнить асинхронные действия вместо/до/после реального диспатча экшена.Я рассматривал два варианта Thunk и Saga и выбрал Thunk. Saga отпала по простой причине - мне не нравятся генераторы. Но принципиально Thunk и Saga ничем не отличаются. Они оба добавляют в поток данных асинхронные действия, при этом поток данных так и остается однонаправленным:
- Пользователь вызвал событие в UI.
- Выполнился редюсер (изменился стейт) либо вызвался экшен thunk или Saga.
- Экшен thunk или Saga сделал асинхронные действия и вызвал один или несколько экшенов редакса что ведет обратно к пункту 1.
Но есть кардинально другой подход - Актеры. Актеры подписываются на изменение состояния и делают асинхронные действия в ответ на изменения какого-то значения в состоянии. Поток данных становится таким:
- Пользователь вызвал событие в UI
- Вызвался экшен redux и поменялось состояние стора.
- Актер подписанный на изменение этого состояния сделал асинхронные действия и вызвал один или несколько экшенов редакса.
На первый взгляд бросается в глаза недостаток актеров в том, что перед асинхронным действием надо задиспатчить дополнительный экшен и изменить состояние стора. Иначе актер не поймет что надо делать. В thunk ничего не мешает сразу начать асинхронные действия.Но те кто делал реальный проект на thunk, а не просто смотрел примеры в документации, знают, что нельзя просто так начать асинхронное действие. Пользователю надо сообщить что программа начала что то делать. И как правило в экшен thunk-а добавляют dispatch({type: ‘START’}) перед асинхронным действием и dispatch({type: ‘END’}) после.Таким образом с точки зрения частоты смены состояния между мидлварой и актером нет никакой разницы.
Как ни крути, при появлении асинхронности будет как минимум два изменения состояния:1. Чтобы сообщить что что-то начало грузится/обрабатываться2. Непосредственно сохранение результата в редакс
Бизнес логика в Redux и ThunkТут все просто. Синхронную логику мы заносим в Redux, асинхронную - в Thunk.Важно отметить, основное что делала бизнес логика в thunk actions - это сравнение предыдущего состояния с новым и в зависимости от результата вызывала другие action.Что не так с Thunk и Saga?Я ничего не имею против Thunk и Saga. Но любая дополнительная библиотека увеличивает базу знаний исходного кода. Для каждой библиотеки каждому разработчику нужно изучить документацию, почитать лучшие практики и получить опыт использования.Поэтому мой минималистичный дух считает что чем меньше подходов используется в приложении тем лучше.
Чем меньше подходов используется при разработке тем лучше.
Разработка актеровПризнаюсь, сначала я пошел по сложному пути. Я начал выдумывать свои подходы к организации бизнес логики. Начал выдумывать некие модули-актеры чтобы в них хранить бизнесс логику. Немаловажное значение имел мой опыт с организаци бизнес логики в thunk.Я подписался на состояние редакса, При изменении состояния, я проверял что поменялось и вызывал dispatch(action).Основной проблемой было придумать как сделать композицию из нескольких актеров и чтобы одни актеры можно было вставлять в другие, и еще передавать параметры от одного актера другому.И когда я решил вынести логику сравнения предыдущего состояния с новым в отдельные хелперные функции, я начал подозревать, что я делаю что-то очень знакомое, что-то, что уже есть готовое, что уже установлено у меня в npm модулях.Я внезапно пришел к тому что уже есть в библиотеке React. Мои модули-актеры - это то что в реакте называется компонентом. Одни компоненты могут вставлятся в другие по условию - это то что мне нужно было для актеров. Есть передача пропсов от одного компонента в другой, есть хуки которые в момент изменения состояния могут задиспатчить экшен в редакс. Все есть.ПризракЕдинственное что мне не нравилось - это определения. Я пытался вынести бизнес логику из react-компонентов, так как это слой UI, а в результате в проекте у меня опять могли получиться везде компоненты: и UI на react-компонентах и актеры на react-компонентах.Мне хотелось четко разделить эти слои, поэтому я сделал две функции:
- ghost - аналог createElement
- ghosts - аналог Fragment
Да, я прям вот так просто присвоил:
export const ghost = createElement;
export const ghosts = createElement.bind(null, Fragment, null);
В переводе с английского означает “призрак”. Мне показалось подходящим названием для противопоставления видимым реакт компонентам.С одной стороны - это отдельное понятие для слоя бизнес логики, не связанное с UI, а с другой стороны включает в себя весь опыт реакта.Вот так выглядит использование:
const AppActor = ({ param1, param2, showModalX }) => ghosts(
ghost(MenuActor, { param1 }),
ghost(PagesActor, { param2 }),
showModalX && ghost(ModalXActor)
)
Никакого jsx и можно спокойно пользоваться привычными хуками.
const HomePageActor = () => {
const dispatch = useDispatch()
useEffect(() => {
const interval = setInterval(() => {
dispatch({type: 'COUNTER_PLUS'})
}, 1000)
return () => clearInterval(interval)
})
// you should explicitly point that this ghost hasn't child ghosts
return null
}
Естественно, я создал себе npm модуль react-ghost с этими двумя строчками и уже применил его на нескольких проектах. Результат меня радует, мне получилось с минимальным усложнением провести четкую границу между бизнесс логикой и UI. Но людям в команде все же приходится объяснять что это, зачем это нужно и что вместо thunk достаточно использовать useEffect.Собственно, поэтому и захотелось поделиться этим подходом и узнать ваше мнение.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, Тестирование IT-систем, Тестирование веб-сервисов, Тестирование мобильных приложений, Agile] Может ли автоматизированное тестирование заменить ручное и другие «дурацкие» вопросы
- [JavaScript, ReactJS] 5 причин использовать LesnJs
- [JavaScript, ReactJS] Почему иногда React/Redux в текущем состоянии give me creeps
- [Информационная безопасность, Разработка веб-сайтов, API, Тестирование веб-сервисов] Nemesida WAF 2021: защита сайтов и API от хакерских атак
- [Разработка веб-сайтов, Usability, Управление сообществом, Дизайн, Научно-популярное] Неуважительный дизайн (перевод)
- [Java, IT-компании] Spring MVC vs Spring WebFlux. Что лучше? Объясняем на пингвинах
- [JavaScript, Maps API, ReactJS] Использование mapbox-gl в React и Next.js
- [Высокая производительность, Разработка веб-сайтов, JavaScript, Программирование, Клиентская оптимизация] Оптимизации в вебе — дорого, сложно, и уже не нужно?
- [Java] Реактивное программирование со Spring (перевод)
- [Java] Реактивное программирование со Spring, часть 4 R2DBC (перевод)
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_reactjs, #_react, #_react.js, #_reactjs, #_biznes_logika (бизнес логика), #_aktery (актеры), #_thunk, #_saga, #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:31
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Где же держать бизнес логику приложения? "Та чего уж там париться - прямо в компонентах." - скажут некоторые. И в некоторых ситуациях это правильное и удобное решение.Но что если мы все-таки хотим чего-то большего? Например, масштабируемости, несколько юзер интерфейсов, лёгкой смены дизайна. Тогда логично разделить приложение на два слоя. Слой бизнес логики и слой представления. Как написано на сайте reactjs.org, React - это библиотека для создания UI. А для модели и бизнес логики написано несколько других замечательных библиотек.Так уж получилось, что проекты, на которых я работал, использовали redux. Поэтому дальше речь пойдет про то, как построить бизнес логику в большом react/redux приложении, и чтобы потом не закрывать рукой глаза при виде того огромного количества редюсеров, экшенов и так далее и тому подобное.СлоиЧтобы не запутаться в своем коде, я привык все делить на разные слои. Вы тоже это делаете, когда стили храните в одном файле, контейнер - в другом, компонент - в третьем, константы в четвертом, переводы в пятом, стейтлесс компоненты - в шестом, а ещё редюсеры, экшены, селекторы, Что? Не храните в разных файлах? Не, ну пусть даже вы назвали это duck подходом и запихнули все в один файл, вы все равно имеете в виду что он состоит из разных слоев.Деление на слои помогает ориентироваться в коде. Вы уже не просто смотрите 125-ю строчку кода вывода таблицы X, а слой контейнера таблицы X.Деление на слои помогает заменять отдельные слои. Например, у вас есть стейтлесс компонент. Пока редакс не готов, вы можете временно подключить к стейтфулл компоненту, а потом стейтфулл компонент заменить на контейнер подключенный к редаксу.Деление на слои помогает разделить работу между разработчиками. Итак, деление на слои - это хорошо.
Размещать бизнес логику в корневом компоненте - это хорошо.
Хуки для бизнес логики - это хорошо
Общее состояние приложения в одном месте - это хорошо.
Как ни крути, при появлении асинхронности будет как минимум два изменения состояния:1. Чтобы сообщить что что-то начало грузится/обрабатываться2. Непосредственно сохранение результата в редакс
Чем меньше подходов используется при разработке тем лучше.
export const ghost = createElement;
export const ghosts = createElement.bind(null, Fragment, null); const AppActor = ({ param1, param2, showModalX }) => ghosts(
ghost(MenuActor, { param1 }), ghost(PagesActor, { param2 }), showModalX && ghost(ModalXActor) ) const HomePageActor = () => {
const dispatch = useDispatch() useEffect(() => { const interval = setInterval(() => { dispatch({type: 'COUNTER_PLUS'}) }, 1000) return () => clearInterval(interval) }) // you should explicitly point that this ghost hasn't child ghosts return null } =========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_reactjs |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:31
Часовой пояс: UTC + 5