Ви цього не робите.
Але ... ви повинні використовувати редукс-сагу :)
Відповідь Дана Абрамова слушна, redux-thunk
але я розповім трохи більше про редукс-сагу, яка є досить подібною, але потужнішою.
Імперативний декларативний VS
- DOM : jQuery є імперативним / React є декларативним
- Монади : IO є імперативним / Free є декларативним
- Ефекти Redux :
redux-thunk
є імперативними / redux-saga
є декларативними
Коли у вас в руках є громовідвідник, як монада IO або обіцянка, ви не можете легко знати, що це зробить, як тільки виконаєте. Єдиний спосіб перевірити грона - це виконати його та знущатися над диспетчером (або над усім зовнішнім світом, якщо він взаємодіє з більшою кількістю матеріалів ...).
Якщо ви використовуєте макети, то ви не займаєтесь функціональним програмуванням.
Через об’єктив побічних ефектів знущання - це прапор, що ваш код нечистий, і в оці функціонального програміста - доказ того, що щось не так. Замість завантаження бібліотеки, яка допоможе нам перевірити, чи є айсберг недоторканим, ми повинні плисти навколо нього. Хардкор TDD / Java хлопець одного разу запитав мене, як ти знущаєшся в Clojure. Відповідь, ми зазвичай цього не робимо. Зазвичай ми бачимо це як знак, що нам потрібно переробити код.
Джерело
Саги (як вони втілилися в життя redux-saga
) є декларативними і подібно до компонентів Free monad або React, їх набагато простіше перевірити без будь-якого знущання.
Дивіться також цю статтю :
в сучасних ПП ми не повинні писати програм - ми повинні писати описи програм, які потім ми можемо вводити, перетворювати та інтерпретувати за бажанням.
(Насправді Редукс-сага - це як гібрид: потік необхідний, але ефекти декларативні)
Плутанина: дії / події / команди ...
У фронтовому світі існує велика плутанина щодо того, як можуть бути пов’язані такі поняття, як бекенд CQRS / EventSourcing та Flux / Redux, здебільшого тому, що в Flux ми використовуємо термін "дія", який іноді може представляти як імперативний код ( LOAD_USER
), так і події ( USER_LOADED
). Я вважаю, що як джерело подій, вам слід розсилати лише події.
Використання саг на практиці
Уявіть додаток із посиланням на профіль користувача. Ідіоматичний спосіб впоратися з цим із кожним проміжним програмним забезпеченням:
redux-thunk
<div onClick={e => dispatch(actions.loadUserProfile(123)}>Robert</div>
function loadUserProfile(userId) {
return dispatch => fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'USER_PROFILE_LOADED', data }),
err => dispatch({ type: 'USER_PROFILE_LOAD_FAILED', err })
);
}
redux-saga
<div onClick={e => dispatch({ type: 'USER_NAME_CLICKED', payload: 123 })}>Robert</div>
function* loadUserProfileOnNameClick() {
yield* takeLatest("USER_NAME_CLICKED", fetchUser);
}
function* fetchUser(action) {
try {
const userProfile = yield fetch(`http://data.com/${action.payload.userId }`)
yield put({ type: 'USER_PROFILE_LOADED', userProfile })
}
catch(err) {
yield put({ type: 'USER_PROFILE_LOAD_FAILED', err })
}
}
Ця сага перекладається на:
кожного разу, коли натискається ім’я користувача, вибирайте профіль користувача та відправте подію із завантаженим профілем.
Як бачите, є деякі переваги redux-saga
.
Використання takeLatest
дозволів для вираження того, що вам цікаво отримати лише дані про останнє натиснуте ім'я користувача (вирішіть проблеми одночасності, якщо користувач дуже швидко натисне на безліч імен користувачів). Такі речі важкі з грозою. Ви могли використати, takeEvery
якщо не хочете такої поведінки.
Ви зберігаєте творців дій чистими. Зауважте, що все ще корисно зберігати actionCreators (у сагах put
та компонентах dispatch
), оскільки це може допомогти вам додати перевірку дій (твердження / потік / typecript) у майбутньому.
Ваш код стає набагато більш перевіреним, оскільки ефекти декларативні
Вам більше не потрібно запускати подібні дзвінки, такі як rpc actions.loadUser()
. Ваш інтерфейс повинен просто відправити те, що сталося. Ми займаємося лише пожежними подіями (завжди в минулому часі!) І більше не робимо дій. Це означає, що ви можете створювати зв'язані "качки" або " Обмежені контексти" і що сага може виступати в якості точки з'єднання між цими модульними компонентами.
Це означає, що вашими переглядами легше керувати, оскільки їм більше не потрібно містити цей шар перекладу між тим, що сталося, і тим, що має відбутися як ефект
Наприклад, уявіть нескінченний вид прокрутки. CONTAINER_SCROLLED
може призвести NEXT_PAGE_LOADED
, але чи справді відповідальність контейнера, який прокручується, вирішує, чи слід завантажувати іншу сторінку чи ні? Тоді він повинен знати про більш складні речі, такі як успішна завантаження чи ні остання сторінка, чи вже є сторінка, яка намагається завантажити, чи не залишилось більше елементів для завантаження? Я не думаю, що так: для максимальної повторної використання контейнер, який прокручується, повинен просто описувати, що він прокручений. Завантаження сторінки - це "діловий ефект" цього прокрутки
Дехто може стверджувати, що генератори можуть приховати стан за межами магазину redux з локальними змінними, але якщо ви почнете упорядковувати складні речі всередині грому, запускаючи таймери тощо, у вас все одно виникне така ж проблема. І є select
ефект, який зараз дозволяє отримати певний стан у вашому магазині Redux.
Саги можуть подорожувати часом, а також дозволяє складати складний журнал потоку та інструменти розробки, над якими зараз працює. Ось декілька простих журналів потоку асинхронізації, які вже реалізовані:
Розв'язка
Саги не тільки замінюють скоромовки. Вони надходять із бекенду / розподілених систем / пошуку подій.
Дуже поширене оману, що саги якраз тут, щоб замінити ваші скоромовки на кращу перевірку. Насправді це лише деталізація редукс-саги. Використання декларативних ефектів краще, ніж загроза для перевірки, але модель саги може бути реалізована поверх імперативного чи декларативного коду.
По-перше, сага - це програмне забезпечення, яке дозволяє координувати тривалі операції (можлива послідовність) та транзакції в різних обмежених контекстах (жаргон дизайну, керований доменом).
Щоб спростити це для фронтового світу, уявіть, що є віджет1 та віджет2. Якщо натиснути якусь кнопку віджета1, це має вплинути на віджет2. Замість того, щоб з'єднати два віджети разом (тобто віджет1 відправляє дію, націлену на віджет2), віджет1 розсилає лише те, що його кнопку було натиснуто. Тоді сага прослухає цей натискання кнопки, а потім оновить віджет2, розпочавши нову подію, про яку знає widget2.
Це додає рівня непрямості, який не є необхідним для простих додатків, але полегшує масштабування складних програм. Тепер ви можете публікувати widget1 та widget2 у різних сховищах npm, щоб вони ніколи не знали один про одного, не маючи їх спільного використання глобального реєстру дій. Два віджети тепер обмежені контекстами, які можуть жити окремо. Їм не потрібно, щоб один одного були послідовними, і їх можна повторно використовувати в інших додатках. Сага - це сполучна точка між двома віджетами, які координують їх у значущий спосіб для вашого бізнесу.
Кілька приємних статей про те, як структурувати додаток Redux, за допомогою якого ви можете використовувати Redux-saga з причини роз’єднання:
Конкретна справа використання: система повідомлень
Я хочу, щоб мої компоненти могли викликати показ сповіщень у додатку. Але я не хочу, щоб мої компоненти сильно поєднувались із системою сповіщень, яка має власні бізнес-правила (максимум 3 повідомлення відображаються одночасно, черга повідомлень, 4 секунди часу показу тощо).
Я не хочу, щоб мої компоненти JSX визначали, коли сповіщення буде показано / приховано. Я просто даю йому можливість запитувати сповіщення і залишати складні правила всередині саги. Такий матеріал досить важко реалізувати за допомогою громів чи обіцянок.
Я тут описав, як це можна зробити за допомогою саги
Чому його називають сагою?
Термін «сага» походить із світу заходу. Я спочатку представив Яссіна (автора «Редукс-саги») в цей термін під час тривалої дискусії .
Спочатку цей термін був введений з документом , модель саги повинна була використовуватися для обробки можливої послідовності розподілених транзакцій, але його використання було розширено на більш широке визначення розробниками бекенда, так що тепер воно також охоплює "менеджера процесів" шаблон (якось оригінальний шаблон саги - це спеціалізована форма менеджера процесів).
Сьогодні термін "сага" заплутаний, оскільки може описувати дві різні речі. Оскільки він використовується в редукс-сазі, він не описує спосіб обробки розподілених транзакцій, а скоріше спосіб координації дій у вашому додатку. redux-saga
можна було також назвати redux-process-manager
.
Дивись також:
Альтернативи
Якщо вам не подобається ідея використання генераторів, але вас цікавить шаблон саги та його властивості роз'єднання, ви також можете досягти того ж за допомогою редукційного спостереження, яке використовує ім'я epic
для опису точно тієї ж картини, але за допомогою RxJS. Якщо ви вже знайомі з Rx, ви будете почувати себе як вдома.
const loadUserProfileOnNameClickEpic = action$ =>
action$.ofType('USER_NAME_CLICKED')
.switchMap(action =>
Observable.ajax(`http://data.com/${action.payload.userId}`)
.map(userProfile => ({
type: 'USER_PROFILE_LOADED',
userProfile
}))
.catch(err => Observable.of({
type: 'USER_PROFILE_LOAD_FAILED',
err
}))
);
Деякі корисні ресурси редукс-саги
2017 радить
- Не зловживайте Redux-saga лише заради використання. Тільки виклики API для тестування не варто.
- Не видаляйте грози з вашого проекту для більшості простих випадків.
- Не соромтеся відправляти громи,
yield put(someActionThunk)
якщо це має сенс.
Якщо вас лякає використання Redux-saga (або Redux-спостерігаемо), але вам просто потрібна схема роз'єднання, перевірте redux-dispatch-Subscribe : це дозволяє прослуховувати відправки та запускати нові відправки у слухача.
const unsubscribe = store.addDispatchListener(action => {
if (action.type === 'ping') {
store.dispatch({ type: 'pong' });
}
});