Redux-Redents — (ще) один модуль для роботи з серверними даними з React-Redux додатків.

React і Redux, останнім часом одні з найпопулярніших buzz-words в світі фронтенда. Тому коли мені знадобилося зробити веб-додаток, яке б відображало дані, отримані з сервера, а також дозволяло б ними маніпулювати (створювати, видаляти та змінювати), я вирішив побудувати його на основі зв'язки React і Redux. Безліч getting-started посібників покривають тільки функціонал створення компонентів, action creators і reducers. Але як тільки справа стосується обміну з сервером, починаються складності — зростає кількість необхідних action creator, редьюсеров. Причому вони дуже схожі один на одного, з миниальными відзнаками. У більшості випадків — тільки в типі (імені) активності. Після того, як я створив третій однаковий набір креаторів і редьюсеров, з'явилося бажання щось змінити. Так народилася ідея реалізації redux-redents.
Початок dictionary reducers
У загальному вигляді 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 і його обробника виглядає так:
  1. створити константу типу action
  2. створити action creator
  3. створити обробник createReducer з заданим типом.
Підтримка набору констант починає дратувати практично відразу. Тим більше, що сенсу в них практично ніякого немає — розробник їх використовують тільки для зв'язки creator і reducer. Таким чином константи можна замінити на угоди про конфігуруванні типів action.
Далі — все action creators для отримання даних з сервера виглядають однаково — створити action з потрібним типом і promise для запиту на сервер. Якщо вони виглядають однаково, то чи не краще автоматизувати процес створення creators, а ще краще зробити універсальний creator?
Об'єднання двох ідей — заміна констант угодами і універсальний creator і привели народження модуля.
Угоди про даних
Якщо для обміну із сервером використовується rest-like api, то для кожного типу даних у нас є однакове число операцій за замовчуванням: index (list), get, post, delete; а у кожної операції є uri і параметри для передачі на сервер. Таким чином можна укласти угоди про умолчаниях:
  • кожен тип даних підтримує стандартний набір операцій
  • для кожної операції визначені правила обчислення url і параметрів
Крім цього потрібно передбачити можливість розширення:
  • додавання операцій
  • конструювання запиту
В результаті з'явився наступний формат:
operation = {
fruit : {}, //all default
vegetable: {
crop : { //custom operation
type: 'CROP_VEGETABLE',
request: (data) => return post(url,data) //custom request
},
index: {
url: url1 //custom url
}
}
} 

Універсальний action creator
Тепер для спрощення життя залишилося реалізувати універсальний actions creator. І знову нам на допомогу приходять угоди:
  • стандартний набір операцій — index, get, post, delete
  • правила обчислення url —
    url = base+entity+'
  • правила передачі параметрів
    • get,delete
      url = url+'/'+data

    • post — відправити 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
Middlewares
Redux middleware — це функція, яка приймає action і наступного в ланцюжку обробки та, яка може обробити action сама і/або передати його далі по ланцюжку обробників. Для обробки promise нам потрібно прийняти вихідний action, встановити обробники promise і модифікувати action, щоб показати, що система знаходиться в стані запиту даних до сервера. Для модифікації можна додавати поля до action, або модифікувати її тип. Я вибрав модифікацію типу.
Promise Middleware
  • змінює тип
    action.type = action.type+'_REQUEST';
  • створює обробник успіху в promise
    переслати в наступний обробник вихідний action з відповіддю сервера
  • створює обробник помилки
    модифікувати тип - action.type=action.type+'_ERROR' і переслати в наступний обработчки помилку, отриману з сервера
  • повертає promise
Promises заробили, дані надходять з сервера, але стало не вистачати можливості викликати action після завершення виконання іншого. Наприклад, оновити дані з сервера після збереження даних на нього ж. Так була придумана Chain Middleware — функція, яка виконує action creator після завершення обробки попереднього action.
Chain Middleware
Для реалізації ланцюжка викликів, останнім параметром до універсального action creator була додана породжує новий action функція, яка приймає на вхід відповідь сервера (якщо він існує) або вихідний action (у протилежному випадку).
Породжує функція викликається тільки в тому випадку якщо оброблюваний action містить поле " status зі значенням 'done' (action.status=='done')
this.entityOperation('fruit','save',fruit,()=>this.props.entityOperation('fruit','index'));

Module
Природним желаением було поділитися цими ідеями та їх реалізацією — так народився модуль redux-redents. Модуль доступний до установки через npm
npm install --save redux-redents

Приклад
В якості прикладу розроблено "додаток" client-demo
git clone https://github.com/kneradovsky/redents/
cd client-demo
npm install
npm start

остання команда збере додаток, запустить сервер розробки і відкриє стартовий URL додатка в браузері
Додаток містить одну сторінку, яка відображає список фруктів і дозволяє додавати, видаляти і редагувати фрукти. Вигляд сторінки на скріншоті нижче:
screenshot
Висновок
Буду радий, якщо мій модуль опиниться корисний. Відкритий для запитань, зауважень та пропозицій по розширенню функціональності. Вихідний код модуля, як завжди, доступний в GitHub репозиторії
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.