- PVSM.RU - https://www.pvsm.ru -
React и Redux, в последнее время одни из самых популярных buzz-words в мире фронтенда. Поэтому когда мне потребовалось сделать веб-приложение, которое бы отображало данные, полученные с сервера, а также позволяло бы ими манипулировать (создавать, удалять и изменять), я решил построить его на основе связки React и Redux. Множество getting-started руководств покрывают только функционал создания компонентов, action creators и reducers. Но как только дело касается обмена с сервером, начинаются сложности — растет количество необходимых action creator, редьюсеров. Причем они очень похожи друг на друга, с миниальными отличиями. В большинстве случаев — только в типе (имени) активности. После того, как я создал третий одинаковый набор креаторов и редьюсеров, то появилось желание что-то изменить. Так родилась идея реализации redux-redents [1].
В общем виде reducers очень похожи друг на друга — принять свой action и создать на его основе новое состояние хранилища. Если рассматривать reducers для обработки ответа с сервера, то они будут отличаться только типом action. Так родилась идея "универсального" редьюсера:
function createReducer(acttype,initialState) {
return (state = initialState, action) => {
if(action.type!=acttype) return state;
return action.res.data;
};
}
const dicts = {
type1 : createReducer(TYPE1_CONSTANT,{}),
type2 : createReducer(TYPE2_CONSTANT,[])
}
const rootReducer = combineReducers({...dicts});
Уже это позволяет не писать одинаковые функции и не городить switch-case конструкции.
Словарь редьюсеров сократил количество одинакового кода, но остались константы для задания типов action. Добавление нового action и его обработчика выглядит так:
Поддержка набора констант начинает раздражать практически сразу. Тем более, что смысла в них практически никакого нет — разработчик их использует только для связки creator и reducer. Таким образом константы можно заменить на соглашения о конфигурировании типов action.
Далее — все action creators для получения данных с сервера выглядят одинаково — создать action с нужным типом и promise для запроса на сервер. Если они выглядят одинаково, то не лучше ли автоматизировать процесс создания creators, а еще лучше сделать универсальный creator?
Объединение двух идей — замена констант соглашениями и универсальный creator и привели рождению модуля.
Если для обмена с сервером используется rest-like api, то для каждого типа данных у нас есть одинаковое число операций по-умолчанию: index (list), get, post, delete; а у каждой операции есть uri и параметры для передачи на сервер. Таким образом можно заключить соглашения об умолчаниях:
Кроме этого нужно предусмотреть возможность расширения:
В результате появился следующий формат:
entities = {
fruit : {}, //all default
vegetable: {
crop : { //custom operation
type: 'CROP_VEGETABLE',
request: (data) => return post(url,data) //custom request
},
index: {
url: url1 //custom url
}
}
}
Теперь для упрощения жизни осталось реализовать универсальный actions creator. И снова нам на помощь приходят соглашения:
url = base+entity+'s'
правила передачи параметров
url = url+'/'+data
Универсальный action creator позволяет сделать следующее:
this.props.entityOperation('fruit','index'); //загружает список фруктов
this.props.entityOperation('fruit','get','apple'); //получает фрукт по имени 'apple'
this.props.entityOperation('fruit','post',{name:'orange',id:'5'}); //обновляет или создает 'orange'
this.props.entityOperation('vegetable','crop',{name:'parrot'}); //выполняет операцию crop над parrot
Creator создает action с promise для отправки/получения данных на сервер. Promise нужно как-то обработать и здесь на поиощь приходят redux middleware
Redux middleware — это функция, которая принимает action и следующий в цепочке обработчик и, которая может обработать action сама и/или передать его дальше по цепочке обработчиков. Для обработки promise нам нужно принять исходный action, установить обработчики promise и модифицировать action, чтобы показать что система находится в состоянии запроса данных к серверу. Для модификации можно либо добавлять поля к action, либо модифицировать ее тип. Я выбрал модификацию типа.
action.type = action.type+'_REQUEST';
переслать в следующий обработчик исходный action с ответом сервера
модифицировать тип - action.type=action.type+'_ERROR' и переслать в следующий обработчки ошибку, полученную с сервера
Promises заработали, данные поступают с сервера, но стало не хватать возможности вызвать action после завершения выполнения другого. Например, обновить данные с сервера после сохранения данных на него же. Так была придумана Chain Middleware — функция, которая выполняет action creator после окончания обработки предыдущего action.
Для реализации цепочек вызовов, последним параметром к универсальному action creator была добавлена порождающая новый action функция, которая принимает на вход ответ сервера (если он существует) или исходный action (в противном случае).
Порождающая функция вызывается только в том, случае если обрабатываемый action содержит поле status со значением 'done' (action.status=='done')
this.entityOperation('fruit','save',fruit,()=>this.props.entityOperation('fruit','index'));
Естесственным желаением было поделиться этими идеями и их реализацией — так родился модуль redux-redents. Модуль доступен к установке через npm
npm install --save redux-redents
В качестве примера разработано "приложение" client-demo [2]
git clone https://github.com/kneradovsky/redents/
cd client-demo
npm install
npm start
последняя команда соберет приложение, запустит сервер разработки и откроет стартовый URL приложения в браузере
Приложение содержит одну страницу, которая отображает список фруктов и позволяет добавлять, удалять и редактировать фрукты. Вид страницы на скриншоте ниже:
Буду рад, если мой модуль окажется полезен. Открыт для вопросов, замечаний и предложений по расширению функциональности. Исходный код модуля, как всегда, доступен в GitHub репозитории [1]
Автор: qualife
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/152154
Ссылки в тексте:
[1] redux-redents: https://github.com/kneradovsky/redents
[2] client-demo: https://github.com/kneradovsky/redents/tree/master/client-demo
[3] Источник: https://habrahabr.ru/post/305074/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.