[ReactJS] Архитектура приложения React Redux
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Предисловие
Это мой первый пост на Хабре, поэтому не судите сильно строго (ну или судите, но конструктивно).
Хотелось бы отметить, что в этом подходе основным преимуществом для меня стало то, что мы четко разграничиваем и делегируем бизнес логику по модулям. Один модуль отвечает за что-то одно и за что-то весьма конкретное. То есть, при таком подходе, в процессе разработки не возникает мысли: «а где мне лучше (правильнее) будет сделать вот это вот?». При таком подходе сразу ясно, где конкретно должна решаться задача/проблема
Файловая структура
Структура проекта +- стандартная. Попытаюсь рассказать вкратце, что используется из чего состоит непосредственно сборка
- Для готовки асинхронных экшнов я использую saga middleware — куда более гибкая и сильная вещь, относительно thunk middleware
- Стилизацией компонентов у меня занимаются css-modules. По сути концепция та же, что и у styled components, но мне как-то оно удобнее
- Прослойкой между контейнерами и компонентами, которая контролирует прокидываемые из стора пропсы в компонент (фильтрация, мемоизация и возможность удобного манипулирования над именами полей любого куска состояния приложения) — reselect
Redux sagas
Redux sagas documentation
Основная суть саг (да и не только) — мы отделяем бизнес логику работы с запросами по модулям. Каждая сага отвечает за свой кусок АПИ. Если бы мне понадобилось получить данные о юзере и вызвать экшн по их успешному получению — я бы это сделал в отдельном модуле usersSagas.js
Основной фактор (для меня) в пользу саг это то, что экшны, по-хорошему должны быть просто функциями, отдающими объект. Если часть экшнов будет выглядеть по-другому (а с использованием thunk оно так и происходит), хочется как-то привести их к оджному виду, ибо как-то это не укладывается в единую концепцию. Хочется вынести логику запросов и работы с данными для запросов в отдельный модуль — для этого и нужны саги
css-modules/styled components
Часто вижу на проектах, что люди пишут правила для стилизации компонентов в каких-то общих модулях css.
Я категорически против этого. Компонент — изолированная система. Он должен быть максимально переиспользуемым. Поэтому, хорошо бы его и стилизовать отдельно.
Reselect
Selector’ы преследуют несколько целей:
- Вынесение логики работы с данными, приходящими в комопнент в отдельный модуль. Это, когда нам нужно получить как бы отфильтрованные данные, но мы не хотим заводить под это кусок стора (это здорово, если мы НЕ хотим заводить отдельный кусок стора под это). До того, как я начал использовать реселект, я делал это внутри компонента. Делал какой-нибудь метод getFilteredItems, в котором возвращал отфильтрованные данные
- Мемоизация и контроль над пропсами, которые будут контролить ререндер компонента
- Гибкость рисунка дерева состояния. К примеру у нас приходят какие-то данные с бека по юзеру. Нам в компоненте нужно получить только какой-то кусок этих данных. К примеру это будет массив friends. Мы пишем селектор под друзей и в контейнерах, которые используют этот кусок стейта его импортируем. Если бы мы не использовали селектор, то мы бы ручками писали название этого поля в каждом контейнере. Представим ситуацию, что логика приложения поменялась, и поле friends переименовали на contacts. Без использования reselect мы будем лазить по всем контейнерам, так или иначе использующими это поле, и менять его название. С использованием реселекта — поменяем парочку реселектов.
Именно из-за последнего пункта я топлю за то, что мы делаем реселект на каждый чих. Реселект — не только для мемоизации, но и для более удобной работы с рисунком дерева состояния приложения
Components and Containers
В классическом подходе к React'у (без использования библиотек для хранения стора, как отдельной сущности) есть два вида компонентов — Presentational и Container-Components. Обычно (как я это понимаю) это не строгое разделение по папкам, а скорее концептуальное разделение.
Presentational — глупые компоненты. Они представляют из себя, по сути, только верстку и отображение данных, которые в них прокинули в качестве пропсов. (пример такого компонента можно глянуть выше в css-modules)
Container-Components — компоненты, инкапсулирующие логику работы с, к примеру, лайф-сайклом компонента. Они отвечают за вызов экшна, отвечающего за запрос на получение данных, к примеру. Возвращают минимум верстки, ибо верстка изолирована в Presentational-Components.
Пример +- Container-Component:
Redux Containers — это, по сути, прослойка между стором редакс и компонентами реакта. В них мы вызываем селекторы и прокидываем экшны в пропсы реакт компонента.
Я ЗА то, чтобы на каждый чих иметь свой контейнер. Во-первых это дает бОльший контроль над пропсами, прокинутыми в компонент, во-вторых — контроль над производительностью, с помощью реселекта.
Часто бывает так, что нам нужно переиспользовать один компонент, но с разными кусками стора. Получается, что для этого нам просто нужно будет написать еще один контейнер и вернуть его там, где необходимо. То есть, связь Многие к одному (многие — контейнеры, один — компонент. По мне — удобно и концептуальненько)
Также хотелось бы привести более частый пример в пользу контейниризации большинства компонентов.
У нас есть массив данных (массив юзеров, к примеру), который мы получаем с некоторой АПИ. Также у нас есть инфинит скролл, от которого заказчик не собирается отказываться. Мы очень долго листали вниз и подгрузили около 10к+ данных. А теперь мы поменяли какое-то свойство одного юзера. Наше приложение будет сильно тормозить, потому что:
- Мы прикрутили глобальный контейнер на всю страницу со списком юзеров
- При изменении одного поля одного элемента массива users у нас в редьюсере users вернулся НОВЫЙ массив с новыми элементами и индексами
- Все компоненты, размещенные в дереве компонента UsersPage будут перерисовываться. В том числе и каждый компонент User (элемент массива)
Как этого избежать?
Мы делаем контейнера на
- массив с юзерами
- элемент массива (один юзер)
После этого мы в компоненте, который завернут в контейнер «массив с юзерами» возвращаем контейнер «элемент массива (один юзер)» с прокинутыми туда key (react required prop), index
В контейнере «элемент массива (один юзер)» в mapStateToProps мы вторым аргументом принимаем ownProps компонента, который контейнер возвращает (среди них index). По индексу мы вытаскиваем из стора напрямую только один элемент массива.
Дальше заоптимизировать перерисовку только изменившегося элемента станет гораздо проще (перерисовывается весь массив, потому что в редьюсере мы сделали какой-нибудь map — возвращающий НОВЫЙ массив с новыми индексами под каждый элемент) — тут нам уже поможет reselect
массив container:
элемент container:
element-selector:
Если есть какие-то дополнения — с удовольствием их почитаю в комментах.
===========
Источник:
habr.com
===========
Похожие новости:
- [ReactJS, Микросервисы] Микрофронтенды. Учимся на ошибках
- [Разработка веб-сайтов, JavaScript, Программирование, .NET, ReactJS] Стилизованные компоненты в React: краткое руководство (перевод)
- [JavaScript, Node.JS, ReactJS, VueJS] Что, черт возьми, такое гидратация и регидратация? (перевод)
- [Разработка веб-сайтов, Программирование, ReactJS, Управление проектами] Как не закопаться в рефакторинге на фронте. Советы новичку
- [PHP, Symfony, ReactJS] SSR: рендеринг ReactJS приложения на бекэнде используя PHP
- [Разработка веб-сайтов, JavaScript, Интерфейсы, ReactJS] Concurrent Mode в React: адаптируем веб-приложения под устройства и скорость интернета
- [JavaScript, Интерфейсы, ReactJS, TypeScript] Когда и CRA мало. Доклад Яндекса
- [JavaScript, Node.JS, PostgreSQL, ReactJS] Javascript платформа Objectum
- [ReactJS, Разработка веб-сайтов] Улучшаем useReducer
- [JavaScript, ReactJS, Программирование, Разработка веб-сайтов] О роли фронтенд-разработчика (перевод)
Теги для поиска: #_reactjs, #_react_redux, #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:02
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Предисловие Это мой первый пост на Хабре, поэтому не судите сильно строго (ну или судите, но конструктивно). Хотелось бы отметить, что в этом подходе основным преимуществом для меня стало то, что мы четко разграничиваем и делегируем бизнес логику по модулям. Один модуль отвечает за что-то одно и за что-то весьма конкретное. То есть, при таком подходе, в процессе разработки не возникает мысли: «а где мне лучше (правильнее) будет сделать вот это вот?». При таком подходе сразу ясно, где конкретно должна решаться задача/проблема Файловая структура Структура проекта +- стандартная. Попытаюсь рассказать вкратце, что используется из чего состоит непосредственно сборка
Redux sagas Redux sagas documentation Основная суть саг (да и не только) — мы отделяем бизнес логику работы с запросами по модулям. Каждая сага отвечает за свой кусок АПИ. Если бы мне понадобилось получить данные о юзере и вызвать экшн по их успешному получению — я бы это сделал в отдельном модуле usersSagas.js Основной фактор (для меня) в пользу саг это то, что экшны, по-хорошему должны быть просто функциями, отдающими объект. Если часть экшнов будет выглядеть по-другому (а с использованием thunk оно так и происходит), хочется как-то привести их к оджному виду, ибо как-то это не укладывается в единую концепцию. Хочется вынести логику запросов и работы с данными для запросов в отдельный модуль — для этого и нужны саги css-modules/styled components Часто вижу на проектах, что люди пишут правила для стилизации компонентов в каких-то общих модулях css. Я категорически против этого. Компонент — изолированная система. Он должен быть максимально переиспользуемым. Поэтому, хорошо бы его и стилизовать отдельно. Reselect Selector’ы преследуют несколько целей:
Именно из-за последнего пункта я топлю за то, что мы делаем реселект на каждый чих. Реселект — не только для мемоизации, но и для более удобной работы с рисунком дерева состояния приложения Components and Containers В классическом подходе к React'у (без использования библиотек для хранения стора, как отдельной сущности) есть два вида компонентов — Presentational и Container-Components. Обычно (как я это понимаю) это не строгое разделение по папкам, а скорее концептуальное разделение. Presentational — глупые компоненты. Они представляют из себя, по сути, только верстку и отображение данных, которые в них прокинули в качестве пропсов. (пример такого компонента можно глянуть выше в css-modules) Container-Components — компоненты, инкапсулирующие логику работы с, к примеру, лайф-сайклом компонента. Они отвечают за вызов экшна, отвечающего за запрос на получение данных, к примеру. Возвращают минимум верстки, ибо верстка изолирована в Presentational-Components. Пример +- Container-Component: Redux Containers — это, по сути, прослойка между стором редакс и компонентами реакта. В них мы вызываем селекторы и прокидываем экшны в пропсы реакт компонента. Я ЗА то, чтобы на каждый чих иметь свой контейнер. Во-первых это дает бОльший контроль над пропсами, прокинутыми в компонент, во-вторых — контроль над производительностью, с помощью реселекта. Часто бывает так, что нам нужно переиспользовать один компонент, но с разными кусками стора. Получается, что для этого нам просто нужно будет написать еще один контейнер и вернуть его там, где необходимо. То есть, связь Многие к одному (многие — контейнеры, один — компонент. По мне — удобно и концептуальненько) Также хотелось бы привести более частый пример в пользу контейниризации большинства компонентов. У нас есть массив данных (массив юзеров, к примеру), который мы получаем с некоторой АПИ. Также у нас есть инфинит скролл, от которого заказчик не собирается отказываться. Мы очень долго листали вниз и подгрузили около 10к+ данных. А теперь мы поменяли какое-то свойство одного юзера. Наше приложение будет сильно тормозить, потому что:
Как этого избежать? Мы делаем контейнера на
После этого мы в компоненте, который завернут в контейнер «массив с юзерами» возвращаем контейнер «элемент массива (один юзер)» с прокинутыми туда key (react required prop), index В контейнере «элемент массива (один юзер)» в mapStateToProps мы вторым аргументом принимаем ownProps компонента, который контейнер возвращает (среди них index). По индексу мы вытаскиваем из стора напрямую только один элемент массива. Дальше заоптимизировать перерисовку только изменившегося элемента станет гораздо проще (перерисовывается весь массив, потому что в редьюсере мы сделали какой-нибудь map — возвращающий НОВЫЙ массив с новыми индексами под каждый элемент) — тут нам уже поможет reselect массив container: элемент container: element-selector: Если есть какие-то дополнения — с удовольствием их почитаю в комментах. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:02
Часовой пояс: UTC + 5