- PVSM.RU - https://www.pvsm.ru -
Автор материала, перевод которого мы сегодня публикуем, говорит, что входит в команду мессенджера Hike [1], которая занимается новыми возможностями приложения. Цель этой команды заключается в том, чтобы воплощать в реальность и исследовать идеи, которые могут понравиться пользователям. Это означает, что действовать разработчикам нужно оперативно, и что им приходится часто вносить изменения в исследуемые ими новшества, которые направлены на то, чтобы сделать работу пользователей как можно более удобной и приятной. Они предпочитают проводить свои эксперименты с применением React Native, так как эта библиотека ускоряет разработку и позволяет использовать один и тот же код на разных платформах. Кроме того, они пользуются библиотекой Redux.
[2]
Когда разработчики из Hike начинают работу над чем-то новым, то, при обсуждении архитектуры исследуемого решения, у них возникает несколько вопросов:
Собственно говоря, найти правильные ответы на все эти вопросы помогает Redux. Архитектура Redux способствует отделению состояния приложения от React. Она позволяет создать глобальное хранилище, находящееся на верхнем уровне приложения и предоставляющее доступ к состоянию для всех остальных компонентов.
Что такое «разделение ответственностей»? Вот что говорит об этом Википедия [3]: «В информатике разделение ответственностей представляет собой процесс разделения компьютерной программы на функциональные блоки, как можно меньше перекрывающие функции друг друга. В более общем случае, разделение ответственностей — это упрощение единого процесса решения задачи путём разделения на взаимодействующие процессы по решению подзадач».
Архитектура Redux позволяет реализовать принцип разделения ответственностей в приложениях, разбивая их на четыре блока, представленные на следующем рисунке.
Архитектура Redux
Вот краткая характеристика этих блоков:
Рассмотрим архитектуру Redux на примере.
В приложении Hike есть экран, отображающий список друзей пользователя. В верхней части этого экрана выводится информация об их количестве.
Экран с информацией о друзьях в приложении Hike
Здесь имеется 3 React-компонента:
FriendRow
— компонент, содержащий имя друга пользователя и некоторые другие сведения о нём.FriendsHeader
— компонент, который выводит надпись «MY FRIENDS» и сведения о количестве друзей.ContainerView
— компонент-контейнер, который объединяет заголовок экрана, представленный компонентом FriendsHeader
, и список друзей, полученный путём обхода массива, содержащего сведения о друзьях пользователя, каждый элемент которого оказывается представленным на экране компонентом FriendRow
.Вот код файла friendsContainer.js [4], иллюстрирующий вышесказанное:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
friends: []
};
}
componentDidMount() {
FriendsService.fetchFriends().then((data) => {
this.setState({
friends: data
});
});
}
render() {
const { friends } = this.state;
return (
<View style={styles.flex}>
<FriendsHeader count={friends.length} text='My friends' />
{friends.map((friend) => (<FriendRow {...friend} />)) }
</View>
);
}
}
Совершенно очевидный способ создания подобной страницы приложения заключается в том, чтобы загрузить данные о друзьях в компонент-контейнер и передать их в виде свойств дочерним компонентам.
Подумаем теперь о том, что эти данные о друзьях могут понадобиться и в каких-то других компонентах, используемых в приложении.
Экран чатов в приложении Hike
Предположим, в приложении имеется экран чатов, который также содержит список друзей. Видно, что и на экране со списком друзей, и на экране чатов используются одни и те же данные. Как поступить в подобной ситуации? У нас есть два варианта:
ComposeChat
, ответственном за вывод списков чатов. Однако такой подход не особенно хорош, так как его применение будет означать дублирование данных и может привести к проблемам с синхронизацией.Оба эти варианта не так уж и привлекательны. Посмотрим теперь на то, как нашу проблему можно решить с использованием архитектуры Redux.
Здесь речь идёт об организации работы с данными с использованием хранилища, создателей действий, редьюсеров и двух компонентов пользовательского интерфейса.
Хранилище содержит загруженные данные о друзьях пользователя. Эти данные могут быть отправлены в любой компонент в том случае, если они там нужны.
В данном случае создатель действия используется для диспетчеризации событий, направленных на сохранение и обновление данных о друзьях. Вот код файла friendsActions.js [5]:
export const onFriendsFetch = (friendsData) => {
return {
type: 'FRIENDS_FETCHED',
payload: friendsData
};
};
Редьюсеры ожидают поступления событий, представляющих диспетчеризованные действия, и обновляют данные о друзьях. Вот код файла friendsReducer.js [6]:
const INITIAL_STATE = {
friends: [],
friendsFetched: false
};
function(state = INITIAL_STATE, action) {
switch(action.type) {
case 'FRIENDS_FETCHED':
return {
...state,
friends: action.payload,
friendsFetched: true
};
}
}
Этот компонент-контейнер просматривает данные о друзьях и обновляет интерфейс при их изменении. Кроме того, он ответственен за загрузку данных из хранилища в том случае, если их у него нет. Вот код файла friendsContainer.js [7]:
class Container extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
if(!this.props.friendsFetched) {
FriendsService.fetchFriends().then((data) => {
this.props.onFriendsFetch(data);
});
}
}
render() {
const { friends } = this.props;
return (
<View style={styles.flex}>
<FriendsHeader count={friends.length} text='My friends' />
{friends.map((friend) => (<FriendRow {...friend} />)) }
</View>
);
}
}
const mapStateToProps = (state) => ({
...state.friendsReducer
});
const mapActionToProps = (dispatch) => ({
onFriendsFetch: (data) => {
dispatch(FriendActions.onFriendsFetch(data));
}
});
export default connect(mapStateToProps, mapActionToProps)(Container);
Этот компонент-контейнер так же пользуется данными из хранилища и реагирует на их обновление.
Для того чтобы довести вышеописанную архитектуру до рабочего состояния может понадобиться день-два, но когда в проект нужно будет вносить изменения, производятся они очень просто и быстро. Если в приложение понадобится добавить новый компонент, использующий данные о друзьях, сделать это можно без необходимости беспокоиться о синхронизации данных или о том, что придётся переделывать другие компоненты. То же самое касается и удаления компонентов.
При использовании Redux каждый блок приложения поддаётся независимому тестированию.
Например, каждый компонент пользовательского интерфейса можно легко подвергнуть модульному тестированию, так как он оказывается независимым от данных. Речь идёт о том, что функция, представляющая такой компонент, всегда возвращает одно и то же представление для одних и тех же данных. Это делает приложение предсказуемым и снижает вероятность ошибок, возникающих при визуализации данных.
Каждый компонент можно всесторонне протестировать, используя самые разные данные. Такое тестирование позволяет выявить скрытые проблемы и способствует обеспечению высокого качества кода.
Надо отметить, что независимому тестированию могут быть подвергнуты не только компоненты, ответственные за визуализацию данных, но и редьюсеры, и создатели действий.
Redux — это замечательно, но используя эту технологию мы столкнулись с некоторыми трудностями.
Для того чтобы реализовать в приложении архитектуру Redux, приходится потратить немало времени, сталкиваясь при этом со всяческими странными понятиями и сущностями.
Это так называемые санки (thunks), редьюсеры (reducers), действия (actions), промежуточные программные слои (middlewares), это функции mapStateToProps
и mapDispatchToProps
, а также многое другое. На то, чтобы всё это изучить, нужно время, а для того, чтобы научиться правильно этим пользоваться, требуется практика. В проекте оказывается очень много файлов, и, например, одно незначительное изменение компонента для визуализации данных может привести к необходимости вносить правки в четыре файла.
В Redux хранилище данных построено с использованием паттерна «синглтон», хотя компоненты могут иметь несколько экземпляров. Чаще всего это не проблема, но в определённых ситуациях подобный подход к хранению данных может создавать некоторые сложности. Например, представим себе, что существуют два экземпляра некоего компонента. Когда в любом из этих экземпляров меняются данные, эти изменения сказываются и на другом экземпляре. В определённых случаях такое поведение может оказаться нежелательным, может понадобиться, чтобы каждый экземпляр компонента пользовался бы собственной копией данных.
Вспомним наш главный вопрос, который заключается в том, стоит ли тратить время и силы на реализацию архитектуры Redux. Мы, в ответ на этот вопрос, говорим Redux «да». Эта архитектура помогает экономить время и силы при разработке и развитии приложений. Использование Redux облегчает жизнь программистов при необходимости частого внесения изменений в приложение, упрощает тестирование. Конечно, архитектура Redux предусматривает наличие немалого объёма шаблонного кода, но она способствует разбиению кода на модули, с которыми удобно работать. Каждый такой модуль может быть протестирован независимо от других, что содействует выявлению ошибок ещё на этапе разработки и позволяет обеспечить высокое качество программ.
Уважаемые читатели! Пользуетесь ли вы Redux в своих проектах?
Автор: ru_vds
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/295899
Ссылки в тексте:
[1] Hike: https://hike.in/
[2] Image: https://habr.com/company/ruvds/blog/426473/
[3] Википедия: https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8
[4] friendsContainer.js: https://gist.github.com/ankitghike/bdadfc6dc86597292f8ed5b1a4479df8
[5] friendsActions.js: https://gist.github.com/ankitghike/750544a718c094ea00f83bbe1d9a8e26
[6] friendsReducer.js: https://gist.github.com/ankitghike/e7f4d2c58cefbea1edc3318cb083d49c
[7] friendsContainer.js: https://gist.github.com/ankitghike/28eb43bf36b41e7671f06d0fbde54b00
[8] Источник: https://habr.com/post/426473/?utm_campaign=426473
Нажмите здесь для печати.