Redux Saga - это библиотека, используемая для обработки побочных эффектов в Redux. Когда вы запускаете действие, что-то меняется в состоянии приложения, и вам может потребоваться сделать что-то, вытекающее из этого изменения состояния.
- Когда использовать Redux Saga
- Базовый пример использования Redux Saga
- Как это работает за кадром
- Базовые помощники
- Параллельное выполнение эффектов
Когда использовать Redux Saga
В приложении, использующемRedux, когда вы запускаете действие, что-то меняется в состоянии приложения.
В этом случае вам может потребоваться сделать что-то, вытекающее из этого изменения состояния.
Например, вы можете захотеть:
- сделать HTTP-вызов на сервер
- отправить событие WebSocket
- получить данные изGraphQLсервер
- сохранить что-нибудь в кеш или локальное хранилище браузера
… Вы поняли идею.
Это все вещи, которые на самом деле не связаны с состоянием приложения или являются асинхронными, и вам нужно переместить их в место, отличное от ваших действий или редукторов (в то время как вы техническимог, это не лучший способ иметь чистую кодовую базу).
Представляем Redux Saga, промежуточное программное обеспечение Redux, которое поможет вам справиться с побочными эффектами.
Базовый пример использования Redux Saga
Чтобы не углубляться в теорию, прежде чем показывать реальный код, я кратко расскажу, как я решил проблему, с которой столкнулся при создании примера приложения.
В чате, когда пользователь пишет сообщение, я немедленно показываю это сообщение на экране, чтобы обеспечить оперативную обратную связь. Это делается черезRedux Action:
const addMessage = (message, author) => ({
type: 'ADD_MESSAGE',
message,
author
})
и состояние меняется через редуктор:
const messages = (state = [], action) => {
switch (action.type) {
case 'ADD_MESSAGE':
return state.concat([{
message: action.message,
author: action.author
}])
default:
return state
}
}
Вы инициализируете Redux Saga, сначала импортируя его, а затем применяясагав качестве промежуточного программного обеспечения для Redux Store:
//...
import createSagaMiddleware from 'redux-saga'
//...
Затем мы создаем промежуточное ПО и применяем его к нашему недавно созданному Redux Store:
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducers,
applyMiddleware(sagaMiddleware)
)
Последний шаг - запуск саги. Мы импортируем его и передаем методу run промежуточного программного обеспечения:
import handleNewMessage from './sagas'
//...
sagaMiddleware.run(handleNewMessage)
Нам просто нужно написать сагу в./sagas/index.js
:
import { takeEvery } from 'redux-saga/effects'
const handleNewMessage = function* handleNewMessage(params) {
const socket = new WebSocket(‘ws://localhost:8989’)
yield takeEvery(‘ADD_MESSAGE’, (action) => {
socket.send(JSON.stringify(action))
})
}
export default handleNewMessage
Этот код означает:каждый развADD_MESSAGE
действие срабатывает, мы отправляем сообщениеWebSocketsсервер, который в этом случае отвечает наlocalhost:8989
.
Обратите внимание на использованиеfunction*
, что не является нормальной функцией, агенератор.
Как это работает за кадром
БытьReduxПромежуточное ПО, Redux Saga может перехватывать действия Redux и внедрять свои собственные функции.
Есть несколько концепций, которые нужно усвоить, и вот основные ключевые слова, которые вы захотите вонзить в свою голову вместе:сага,генератор,промежуточное ПО,обещать,Пауза,продолжить,эффект,отправлять,действие,выполнено,решено,урожай,уступил.
Асагаэто некая «история», которая реагирует наэффектчто ваш код вызывает. Это может содержать что-то из того, о чем мы говорили раньше, например, HTTP-запрос или некоторую процедуру, которая сохраняется в кеш.
Мы создаемпромежуточное ПОсо спискомсагиto run, которых может быть один или несколько, и мы подключаем это промежуточное ПО к хранилищу Redux.
Асагаэтогенераторфункция. Когдаобещатьзапущен иуступил, промежуточное ПОприостанавливаетвсагадообещатьявляетсярешено.
Однаждыобещатьявляетсярешенопромежуточное ПОвозобновляетсясага, до следующегоурожайзаявление найдено, и вот оноприостановленныйснова, пока егообещать решает.
Внутри кода саги вы создадитепоследствияиспользуя несколько специальных вспомогательных функций, предоставляемыхredux-saga
упаковка. Для начала мы можем перечислить:
takeEvery()
takeLatest()
take()
call()
put()
Когдаэффектвыполняется,сагаявляетсяприостановленодоэффектявляетсявыполнено.
Например:
import { takeEvery } from 'redux-saga/effects'
const handleNewMessage = function* handleNewMessage(params) {
const socket = new WebSocket(‘ws://localhost:8989’)
yield takeEvery(‘ADD_MESSAGE’, (action) => {
socket.send(JSON.stringify(action))
})
}
export default handleNewMessage
Когдапромежуточное ПОвыполняетhandleNewMessage
сага, этоостанавливаетсянаyield takeEvery
инструкция иждет(асинхронно, конечно) до тех пор, покаADD_MESSAGE
действиеотправлен. Затем он выполняет обратный вызов, исагаможетпродолжить.
Базовые помощники
Помощники - это абстракции поверх низкоуровневых API-интерфейсов саги.
Давайте познакомимся с самыми основными помощниками, которые вы можете использовать для запуска ваших эффектов:
takeEvery()
takeLatest()
take()
put()
call()
takeEvery()
takeEvery()
, используемый в некоторых примерах, является одним из таких помощников.
В коде:
import { takeEvery } from 'redux-saga/effects'
function* watchMessages() {
yield takeEvery(‘ADD_MESSAGE’, postMessageToServer)
}
ВwatchMessages
генератор останавливается до тех пор, покаADD_MESSAGE
боевые пожары, икаждый разон срабатывает, он вызоветpostMessageToServer
функция, бесконечно и одновременно (нет необходимостиpostMessageToServer
чтобы завершить его выполнение до того, как можно будет запустить новый раз)
takeLatest()
Еще один популярный помощник -takeLatest()
, что очень похоже наtakeEvery()
но позволяет одновременно запускать только один обработчик функции, избегая параллелизма. Если другое действие запускается, когда обработчик все еще работает, он отменяет его и запускается снова с последними доступными данными.
Как и в случае сtakeEvery()
, генератор никогда не останавливается и продолжает запускать эффект при выполнении указанного действия.
take()
take()
отличается тем, что ждет только один раз. Когда происходит ожидаемое действие, обещание разрешается и итератор возобновляет работу, поэтому он может перейти к следующему набору инструкций.
put()
Отправляет действие в магазин Redux. Вместо передачи в хранилище Redux или действия отправки в сагу вы можете просто использоватьput()
:
yield put({ type: 'INCREMENT' })
yield put({ type: "USER_FETCH_SUCCEEDED", data: data })
который возвращает простой объект, который вы можете легко проверить в своих тестах (подробнее о тестировании позже).
call()
Если вы хотите вызвать какую-либо функцию в саге, вы можете сделать это, используя простой вызов функции, возвращающий обещание:
delay(1000)
но это не очень хорошо с тестами. Вместо,call()
позволяет обернуть этот вызов функции и вернуть объект, который можно легко проверить:
call(delay, 1000)
возвращается
{ CALL: {fn: delay, args: [1000]}}
Параллельное выполнение эффектов
Параллельное выполнение эффектов возможно с помощьюall()
иrace()
, которые сильно различаются по своему назначению.
all()
Если вы напишете
import { call } from 'redux-saga/effects'
const todos = yield call(fetch, ‘/api/todos’)
const user = yield call(fetch, ‘/api/user’)
второйfetch()
вызов не будет выполнен, пока первый не завершится успешно.
Чтобы выполнить их параллельно, оберните их вall()
:
import { all, call } from 'redux-saga/effects'
const [todos, user] = yield all([
call(fetch, ‘/api/todos’),
call(fetch, ‘/api/user’)
])
all()
не будет решен, пока обаcall()
возвращаться.
race()
race()
отличается отall()
не дожидаясь ответа всех помощников. Он просто ждет, пока один из них вернется, и все готово.
Это гонка, чтобы увидеть, кто из них финиширует первым, а потом мы забываем о других участниках.
Обычно он используется для отмены фоновой задачи, которая выполняется вечно, пока что-то не произойдет:
import { race, call, take } from 'redux-saga/effects'
function* someBackgroundTask() {
while(1) {
//…
}
}
yield race([
bgTask: call(someBackgroundTask),
cancel: take(‘CANCEL_TASK’)
])
когдаCANCEL_TASK
действие, мы останавливаем другую задачу, которая в противном случае выполнялась бы вечно.
Скачать мою бесплатнуюСправочник по React
Больше руководств по реакции:
- Пример простого приложения React: получение информации о пользователях GitHub через API
- Создайте простой счетчик с помощью React
- Настройка VS Code для разработки на React
- Как передать реквизиты дочернему компоненту через React Router
- Создайте приложение с Electron и React
- Учебник: создание электронной таблицы с помощью React
- Дорожная карта для изучения React
- Узнайте, как использовать Redux
- Начало работы с JSX
- Стилизованные компоненты
- Введение в Redux Saga
- Введение в React Router
- Введение в React
- Компоненты React
- Виртуальный DOM
- Реагировать на события
- Состояние реакции
- React Props
- Фрагмент реакции
- API контекста React
- React PropTypes
- Концепции React: декларативный
- Реагировать: как показать другой компонент при нажатии
- Как зацикливаться внутри React JSX
- Props vs State в React
- Что лучше: jQuery или React?
- Сколько JavaScript вам нужно знать, чтобы использовать React?
- Введение в Гэтсби
- Как сослаться на элемент DOM в React
- Однонаправленный поток данных в React
- Реагировать на компоненты более высокого порядка
- Реагировать на события жизненного цикла
- Концепция React: неизменность
- Концепция React: чистота
- Введение в React Hooks
- Введение в приложение create-react-app
- Концепция React: композиция
- React: презентационные и контейнерные компоненты
- Разделение кода в React
- Рендеринг на стороне сервера с помощью React
- Как установить React
- CSS в React
- Использование SASS в React
- Обработка форм в React
- React StrictMode
- Реагировать на порталы
- React Render Props
- Тестирование компонентов React
- Как передать параметр обработчикам событий в React
- Как обрабатывать ошибки в React
- Как вернуть несколько элементов в JSX
- Условный рендеринг в React
- React, как передать props дочерним компонентам
- Как получить значение элемента ввода в React
- Как использовать хук useState React
- Как использовать хук useCallback React
- Как использовать хук useEffect React
- Как использовать хук useMemo React
- Как использовать хук useRef React
- Как использовать хук useContext React
- Как использовать хук useReducer React
- Как подключить приложение React к бэкэнду в том же источнике
- Учебное пособие по маршрутизатору Reach Router
- Как использовать инструменты разработчика React
- Как научиться React
- Как отлаживать приложение React
- Как отрендерить HTML в React
- Как исправить ошибку `dangerousSetInnerHTML` не соответствовало в React
- Как я исправил проблему с состоянием формы входа в React и автозаполнением браузера
- Как настроить HTTPS в приложении React на локальном хосте
- Как исправить ошибку "не удается обновить компонент при рендеринге другого компонента" в React
- Могу ли я использовать перехватчики React внутри условного оператора?
- Использование useState с объектом: как обновить
- Как перемещать блоки кода с помощью React и Tailwind
- React, сфокусируйте элемент в React при добавлении в DOM
- Реагировать, редактировать текст по двойному щелчку