Повторение по селекторам
Статья в документации: https://redux.js.org/usage/deriving-data-selectors
Селекторы - это обычные функции, которые получают на вход состояние приложения (чаще полное состояние, а не отдельные части) и возвращают какие-то данные из него (возможно, обработанные). Например, селектор может возвращать полный список элементов TODO-листа, а может фильтровать его по статусу.
Зачем нужны селекторы
Использование селекторов позволяет сохранять состояние максимально чистым (все, что можно вычислить, вычисляется в селекторах), а также дает возможность переиспользовать логику выбора данных (один селектор можно использовать в нескольких местах). Это особенно удобно, если селектор достает данные из глубин состояния (третий-четвертый уровень вложенности).
В общем, в селекторах инкапсулируется логика получения данных из состояния. Они знают структуру состояния и как достать нужные данные. В идеале, это знание не должно выходить за пределы селекторов и редьюсера. Компоненты не должны заботиться об устройстве состояния, они хотят только получать нужные данные.
Организация селекторов
Документация рекомендует давать функция-селекторам осмысленные имена, начинающиеся с
Селекторы обычно располагаются в файлах слайсов, рядом с логикой редьюсера. Отдельные селекторы могут находиться и в компонентах (специфичные селекторы, которые не переиспользуются в других местах). Селекторы также могут находиться внутри thunks, так как там есть доступ к полному состоянию.
Селекторы применяются вместе с хуком
Проблемы
Внутри селекторов могут производиться достаточно сложные вычисления, поэтому следует минимизировать количество их запусков. Однако селекторы запускаются при каждом изменении состояния, причем все селекторы, даже те, которые не имеют отношения к изменившимся данным.
Хук
Чтобы избежать этого, нужна мемоизация селекторов. (Мемоизация не нужна, если селектор возвращает примитивное значение).
Мемоизация
Для мемоизации мы используем библиотеку Reselect (есть и альтернативы, которые указаны в статье). Она предоставляет функцию
#управлениесостоянием #документация #redux #оптимизация
Статья в документации: https://redux.js.org/usage/deriving-data-selectors
Селекторы - это обычные функции, которые получают на вход состояние приложения (чаще полное состояние, а не отдельные части) и возвращают какие-то данные из него (возможно, обработанные). Например, селектор может возвращать полный список элементов TODO-листа, а может фильтровать его по статусу.
Зачем нужны селекторы
Использование селекторов позволяет сохранять состояние максимально чистым (все, что можно вычислить, вычисляется в селекторах), а также дает возможность переиспользовать логику выбора данных (один селектор можно использовать в нескольких местах). Это особенно удобно, если селектор достает данные из глубин состояния (третий-четвертый уровень вложенности).
В общем, в селекторах инкапсулируется логика получения данных из состояния. Они знают структуру состояния и как достать нужные данные. В идеале, это знание не должно выходить за пределы селекторов и редьюсера. Компоненты не должны заботиться об устройстве состояния, они хотят только получать нужные данные.
Организация селекторов
Документация рекомендует давать функция-селекторам осмысленные имена, начинающиеся с
select
.Селекторы обычно располагаются в файлах слайсов, рядом с логикой редьюсера. Отдельные селекторы могут находиться и в компонентах (специфичные селекторы, которые не переиспользуются в других местах). Селекторы также могут находиться внутри thunks, так как там есть доступ к полному состоянию.
Селекторы применяются вместе с хуком
useSelector
.Проблемы
Внутри селекторов могут производиться достаточно сложные вычисления, поэтому следует минимизировать количество их запусков. Однако селекторы запускаются при каждом изменении состояния, причем все селекторы, даже те, которые не имеют отношения к изменившимся данным.
Хук
useSelector
сравнивает результат выполнения селектора с предыдущим (используя строгое равенство `===`). Если значения отличаются, выполняется перерендер. То есть селектор не должен возвращать новое значение, если ничего не изменилось. Но тут появляется проблема, например, с методами массивов (map, filter), которые всегда возвращают новый массив. Даже если список отфильтрованных элементов не изменился, хук увидит новый массив и посчитает, что нужен перерендер.Чтобы избежать этого, нужна мемоизация селекторов. (Мемоизация не нужна, если селектор возвращает примитивное значение).
Мемоизация
Для мемоизации мы используем библиотеку Reselect (есть и альтернативы, которые указаны в статье). Она предоставляет функцию
createSelector
, которая принимает любое количество входных селекторов (для проверки изменения различных частей состояния) и один выходной (собственно для выборки данных). Выходной селектор вызывается только в том случае, если входные селекторы возвращают изменившиеся результаты.#управлениесостоянием #документация #redux #оптимизация
redux.js.org
Deriving Data with Selectors | Redux
Usage > Redux Logic > Selectors: deriving data from the Redux state
👍3
Реализация функциональности Шаг назад
Статья в документации: https://redux.js.org/usage/implementing-undo-history
В статье описано, как реализовать в Redux-приложении функционал Шаг назад/вперед. В Redux это особенно просто, так как все состояние хранится в одном объекте и при каждом изменении оно представлено новым объектом - значит легко хранить последовательность состояний. К тому же вся логика управления состоянием находится в функции (редьюсере). Здесь можно использовать паттерн декоратор, чтобы добавить новый функционал, не ломая основной.
Глобально паттерн выглядит так:
Текущее состояние хранится в поле
В статье дана полная реализация алгоритма. Также можно воспользоваться готовой реализацией из пакета redux-undo.
#управлениесостоянием #redux #документация #паттерны
Статья в документации: https://redux.js.org/usage/implementing-undo-history
В статье описано, как реализовать в Redux-приложении функционал Шаг назад/вперед. В Redux это особенно просто, так как все состояние хранится в одном объекте и при каждом изменении оно представлено новым объектом - значит легко хранить последовательность состояний. К тому же вся логика управления состоянием находится в функции (редьюсере). Здесь можно использовать паттерн декоратор, чтобы добавить новый функционал, не ломая основной.
Глобально паттерн выглядит так:
{
past: [T, T],
present: T,
future: [T]
}
Текущее состояние хранится в поле
present
. При каждом изменении старое состояние добавляется в массив past
, а present
изменяется. Если потребуется сделать Шаг назад, мы просто возьмем последнее состояние из past
и установим его в present
. При этом текущее состояние нужно сохранить в future
, чтобы к нему можно было вернуться.В статье дана полная реализация алгоритма. Также можно воспользоваться готовой реализацией из пакета redux-undo.
#управлениесостоянием #redux #документация #паттерны
redux.js.org
Implementing Undo History | Redux
- Completion of the "Redux Fundamentals" tutorial
👍3
По TypeScript получилось довольно много материала (и еще будет 😄), поэтому добавлены новые теги для сложных тем: #infer, #дженерики и #tsдекораторы
Все теги тут: https://yangx.top/react_junior/84
Все теги тут: https://yangx.top/react_junior/84
Telegram
React Junior
Список тегов для более удобного поиска постов
👉 Основные понятия
#компоненты
#jsx
#жизненныйциклкомпонента
#виртуальныйdom
#состояние
#обработкасобытий
#потокданных
#ключи
#формы
#рефы
#hoc
#рендерпропсы
#контекст
#порталы
#хуки
👉 Управление состоянием…
👉 Основные понятия
#компоненты
#jsx
#жизненныйциклкомпонента
#виртуальныйdom
#состояние
#обработкасобытий
#потокданных
#ключи
#формы
#рефы
#hoc
#рендерпропсы
#контекст
#порталы
#хуки
👉 Управление состоянием…
🔥3
Стоит ли использовать Redux в Next.js-приложении?
Статья (англ.): https://javascript.plainenglish.io/should-you-use-redux-in-next-js-5e57201c34da
Автор статьи считает, что нам нужно отказаться от Redux в Next.
Причина #1: В нем нет особого смысла, так как благодаря серверному рендерингу мы сразу же, еще до рендеринга страницы, получаем нужные данные.
Причина #2: У Redux есть ряд альтернатив - более легких, нативных и удобных. Например, React Context или Local Storage. В большинстве случаев их функциональности более чем достаточно. Для более сложных сценариев, например, для загрузки данных на клиенте есть библиотеки вроде swr или react-query.
Причина #3: Redux внутри Next сложно хорошо настроить и оптимизировать.
#nextjs #ссылки #redux
Статья (англ.): https://javascript.plainenglish.io/should-you-use-redux-in-next-js-5e57201c34da
Автор статьи считает, что нам нужно отказаться от Redux в Next.
Причина #1: В нем нет особого смысла, так как благодаря серверному рендерингу мы сразу же, еще до рендеринга страницы, получаем нужные данные.
Причина #2: У Redux есть ряд альтернатив - более легких, нативных и удобных. Например, React Context или Local Storage. В большинстве случаев их функциональности более чем достаточно. Для более сложных сценариев, например, для загрузки данных на клиенте есть библиотеки вроде swr или react-query.
Причина #3: Redux внутри Next сложно хорошо настроить и оптимизировать.
#nextjs #ссылки #redux
Medium
Should You Use Redux in Next.js?
Do you even need it?
👍2
Как настроить Redux в NextJS
Совсем недавно мы видели мнение, что Redux в Next.js использовать не стоит: https://yangx.top/c/1218235935/551
Но если вам все-таки хочется, то вот руководство по настройке (англ.): https://medium.com/how-to-react/how-to-setup-redux-in-nextjs-5bce0d82b8de
Выглядит не особо сложно. Помимо обычных настроек Redux нам понадобится еще функция
Теперь нужно обернуть приложение в провайдер хранилища. Сделать это лучше всего в файле _app.js. В провайдер передаем
А чтобы все работало, нужно использовать компонент высшего порядка
Вот и все, теперь можно пользоваться плюшками Redux.
#nextjs #redux #статьи
Совсем недавно мы видели мнение, что Redux в Next.js использовать не стоит: https://yangx.top/c/1218235935/551
Но если вам все-таки хочется, то вот руководство по настройке (англ.): https://medium.com/how-to-react/how-to-setup-redux-in-nextjs-5bce0d82b8de
Выглядит не особо сложно. Помимо обычных настроек Redux нам понадобится еще функция
createWrapper
из пакета next-redux-wrapper.const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
const wrapper = createWrapper(function() {
return store;
});
Теперь нужно обернуть приложение в провайдер хранилища. Сделать это лучше всего в файле _app.js. В провайдер передаем
store
.А чтобы все работало, нужно использовать компонент высшего порядка
wrapper.withRedux
.import { Provider } from "react-redux";
function MyApp({ Component, pageProps }) {
return (
Provider store={store}
Component {...pageProps}
)
}
export default wrapper.withRedux(MyApp);
Вот и все, теперь можно пользоваться плюшками Redux.
#nextjs #redux #статьи
👍3
Redux-saga. Общий обзор
Быстрый экскурс в Redux-saga: что за штука, зачем нужна, как примерно работает.
Saga - это аналог Redux-thunk, штука, которая помогает нам управлять сайд-эффектами в redux. Когда мы диспатчим в хранилище какой-то экшен и хотим, чтобы при этом запрашивались с сервера какие-то данные - нам сюда.
С thunk уже разбирались: он позволяет вместо объекта экшена создавать функцию. Миддлвар смотрит на экшен и, если видит, что это функция, не пропускаего его в стор, а вызывает, передавая аргументами полезные методы и данные стора. (самописный пример)
То есть с thunk нам необходимо усложнять action creators, всю логику мы кладем туда.
Saga работает по-другому: она "прослушивает" экшены и, если видит нужный, запускает обработчик для него. Соответственно, в саге есть вотчеры (следят за экшенами) и воркеры (содержат бизнес-логику).
А еще есть эффекты - это библиотечные функции (встроены в Saga), которые позволяют нам выстроить правильную последовательность действий внутри воркеров, в общем, утилиты.
Структура saga
Сага имеет древовидную структуру, то есть у нас есть корневая сага, которая "ветвится" - устанавливает какие-то базовые вотчеры и воркеры - дочерние саги. Каждая из дочерних саг тоже может "ветвиться". У такого подхода есть полезные плюшки - весь рабочий процесс делится на "транзакции", которые можно отменять. На это чуть позже посмотрим поближе.
Важно: саги - это функции-генераторы! То есть они могут прерывать процесс своего выполнения. Освежить память по генераторам можно, например, здесь: https://yangx.top/furrycat/456
Установка и подключение
Нам нужен пакет redux-saga. Сага подключается к redux-хранилищу как миддлвар.
Посмотреть можно здесь: https://codesandbox.io/s/redux-saga-getting-started-react-junior-h3l596?file=/src/store/index.js
- Сначала создаем миддлвар с помощью функции
- добавляем его к остальным с помощью
- передаем их в
- После создания хранилища нужно еще запустить саговский миддлвар, вызвав у него метод
#redux #управлениесостоянием #примерыкода #saga
Быстрый экскурс в Redux-saga: что за штука, зачем нужна, как примерно работает.
Saga - это аналог Redux-thunk, штука, которая помогает нам управлять сайд-эффектами в redux. Когда мы диспатчим в хранилище какой-то экшен и хотим, чтобы при этом запрашивались с сервера какие-то данные - нам сюда.
С thunk уже разбирались: он позволяет вместо объекта экшена создавать функцию. Миддлвар смотрит на экшен и, если видит, что это функция, не пропускаего его в стор, а вызывает, передавая аргументами полезные методы и данные стора. (самописный пример)
То есть с thunk нам необходимо усложнять action creators, всю логику мы кладем туда.
Saga работает по-другому: она "прослушивает" экшены и, если видит нужный, запускает обработчик для него. Соответственно, в саге есть вотчеры (следят за экшенами) и воркеры (содержат бизнес-логику).
А еще есть эффекты - это библиотечные функции (встроены в Saga), которые позволяют нам выстроить правильную последовательность действий внутри воркеров, в общем, утилиты.
Структура saga
Сага имеет древовидную структуру, то есть у нас есть корневая сага, которая "ветвится" - устанавливает какие-то базовые вотчеры и воркеры - дочерние саги. Каждая из дочерних саг тоже может "ветвиться". У такого подхода есть полезные плюшки - весь рабочий процесс делится на "транзакции", которые можно отменять. На это чуть позже посмотрим поближе.
Важно: саги - это функции-генераторы! То есть они могут прерывать процесс своего выполнения. Освежить память по генераторам можно, например, здесь: https://yangx.top/furrycat/456
Установка и подключение
Нам нужен пакет redux-saga. Сага подключается к redux-хранилищу как миддлвар.
Посмотреть можно здесь: https://codesandbox.io/s/redux-saga-getting-started-react-junior-h3l596?file=/src/store/index.js
- Сначала создаем миддлвар с помощью функции
createSagaMiddleware
, - добавляем его к остальным с помощью
applyMiddleware
, - передаем их в
createStore
. - После создания хранилища нужно еще запустить саговский миддлвар, вызвав у него метод
sagaMiddleware.run()
.#redux #управлениесостоянием #примерыкода #saga
Telegram
React Junior
Экшены-функции
Еще один вариант - переместить код, связанный с экшеном в функцию-создатель экшена. Но тут есть проблема - у функций-создателей нет доступа к методу store.dispatch.
Эту задачу тоже можно решить с помощью миддлваров. Нужно сделать так, чтобы…
Еще один вариант - переместить код, связанный с экшеном в функцию-создатель экшена. Но тут есть проблема - у функций-создателей нет доступа к методу store.dispatch.
Эту задачу тоже можно решить с помощью миддлваров. Нужно сделать так, чтобы…
👍5
Redux-saga. Структура саг и эффекты
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты и получили список уведомлений).
У нас есть корневая сага (`rootSaga`) - корневой процесс.
Сага может вызывать другие саги, формируя древовидную структуру, причем вложенные саги связаны с родительской.
Принципиальная схема саги: разделяем вотчер и воркер. Внутри них нам понадобятся "эффекты" - это очень крутая штука, которая позволяет наворачивать логику в зависимости от срабатывания экшенов в нужной для нас последовательности.
Например, чтобы "подписаться" на экшен, нужен эффект take:
Корневая сага запускает вотчер, который подписывается на экшен с типом
Эта штука сработает только один раз, при первом же экшене
Если нужно, чтобы срабатывало при каждом, потребуется другой эффект -
А внутри воркера можем получить доступ к хранилищу и вывести текущее значение счетчика с помощью эффекта
Немного усложним и представим, что воркер выполняет некую асинхронную работу, которая занимает время, например, отправляет запрос на сервер. И мы не хотим, чтобы два запроса выполнялось одновременно. Для этого есть эффекты
Кстати, для создания искусственной задержки есть эффект
Так, экшены отслеживать умеем, из стора читать умеем, надо еще диспатчить. Это тоже можно - с эффектом
Например, реализуем асинхронный инкремент. Тут нам понадобится и
Для начала, пожалуй, достаточно.
#redux #управлениесостоянием #примерыкода #saga
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты и получили список уведомлений).
У нас есть корневая сага (`rootSaga`) - корневой процесс.
Сага может вызывать другие саги, формируя древовидную структуру, причем вложенные саги связаны с родительской.
Принципиальная схема саги: разделяем вотчер и воркер. Внутри них нам понадобятся "эффекты" - это очень крутая штука, которая позволяет наворачивать логику в зависимости от срабатывания экшенов в нужной для нас последовательности.
Например, чтобы "подписаться" на экшен, нужен эффект take:
import { take } from "@redux-saga/core/effects"
function workerSaga() {
console.log('count increased')
yield;
}
function* watcherSaga() {
yield take(INCREMENT_COUNTER);
yield workerSaga();
}
function* rootSaga() {
yield watcherSaga();
}
Корневая сага запускает вотчер, который подписывается на экшен с типом
INCREMENT_COUNTER
. Пока этот экшен не сработает, остальной код в вотчере НЕ вызовется. То есть мы будем ждать экшена и только потом запустим воркер.Эта штука сработает только один раз, при первом же экшене
INCREMENT_COUNTER
.Если нужно, чтобы срабатывало при каждом, потребуется другой эффект -
takeEvery
:
function* watcherSaga() {
yield takeEvery(INCREMENT_COUNTER, workerSaga)
}
А внутри воркера можем получить доступ к хранилищу и вывести текущее значение счетчика с помощью эффекта
select
:
function* workerSata() {
const count = yield select((state) => state.counter.value);
console.log("count increased", count);
}
Немного усложним и представим, что воркер выполняет некую асинхронную работу, которая занимает время, например, отправляет запрос на сервер. И мы не хотим, чтобы два запроса выполнялось одновременно. Для этого есть эффекты
takeLatest
и takeLeading
- используем их вместо takeEvery
. takeLeading
не запускает воркер заново, пока выполняется предыдущий.takeLatest
отменяет текущий запрос, если пришел новый экшен.Кстати, для создания искусственной задержки есть эффект
delay
.Так, экшены отслеживать умеем, из стора читать умеем, надо еще диспатчить. Это тоже можно - с эффектом
put
. Например, реализуем асинхронный инкремент. Тут нам понадобится и
delay
, и takeLatest
, и put
.
function* asyncIncrementSaga() {
yield delay(1000);
yield put({ type: INCREMENT_COUNTER });
}
function* watcher() {
yield takeLatest(INCREMENT_COUNTER_ASYNC, asynIncrementSaga)
}
Для начала, пожалуй, достаточно.
#redux #управлениесостоянием #примерыкода #saga
👍4
Redux-saga. Эффект call и тестирование саг
Продолжаем разбор redux-saga.
В прошлый раз мы узнали:
- что саги объединяются в виде дерева - из одной саги могут "прорастать" другие. Такая структура позволяет при ошибке изолировать проблемную ветку, об этом поговорим чуть позже.
- что саги делятся на вотчеры (подписка на события) и воркеры (обработчики событий).
- что у нас есть куча встроенных хелперов (эффектов) и для вотчеров (take/takeEvery/takeLatest/takeLeading), и для воркеров (delay/select/put). Эффекты - это обычные объекты, содержащие инструкции для миддлвара.
Теперь чуть поближе посмотрим на эффекты на примере эффекта
Call
А можем через
Функциональной разницы нет, запрос и так, и так будет выполнен. Но есть разница техническая.
Например, мы хотим протестировать сагу
В первом случае в переменной
Эффект возвращает нам просто объект с инструкциями, который проще тестировать. А сам вызов произойдет уже в миддлваре. Для тестирования мы можем просто снова создать эффект с теми же параметрами:
При таком подходе нам не придется подменять метод API.fetch, так как мы его даже не вызываем.
Контекст выполнения
В
Apply
Кроме
#redux #управлениесостоянием #примерыкода #saga
Продолжаем разбор redux-saga.
В прошлый раз мы узнали:
- что саги объединяются в виде дерева - из одной саги могут "прорастать" другие. Такая структура позволяет при ошибке изолировать проблемную ветку, об этом поговорим чуть позже.
- что саги делятся на вотчеры (подписка на события) и воркеры (обработчики событий).
- что у нас есть куча встроенных хелперов (эффектов) и для вотчеров (take/takeEvery/takeLatest/takeLeading), и для воркеров (delay/select/put). Эффекты - это обычные объекты, содержащие инструкции для миддлвара.
Теперь чуть поближе посмотрим на эффекты на примере эффекта
call
и немного затронем тему тестирования саг.Call
Call
- встроенный эффект, который работает как стандартный метод Function.call
. Он принимает функцию, которую нужно вызывать и набор аргументов для вызова. То есть мы можем вызвать функцию напрямую:
function* fetchProducts() {
const products = yield API.fetch('/products');
}
А можем через
call
:
function* fetchProducts() {
const products = yield call(API.fetch, '/products');
}
Функциональной разницы нет, запрос и так, и так будет выполнен. Но есть разница техническая.
Например, мы хотим протестировать сагу
fetchProducts
. Это генератор, поэтому нужно использовать метод next
для получения каждого промежуточного результата:
const iterator = fetchProducts();
const res = iterator.next();
В первом случае в переменной
res
окажется промис. Во втором - объект эффекта, который выглядит примерно так:
{
CALL: {
fn: Api.fetch,
args: ['./products']
}
}
Эффект возвращает нам просто объект с инструкциями, который проще тестировать. А сам вызов произойдет уже в миддлваре. Для тестирования мы можем просто снова создать эффект с теми же параметрами:
const { call } from 'redux-saga/effects';
const iterator = fetchProducts();
const res = iterator.next();
assert.deepEqual(
res.value,
call(API.fetch, '/products'),
"fetchProducts should yield an Effect call(API.fetch, './products')"
)
При таком подходе нам не придется подменять метод API.fetch, так как мы его даже не вызываем.
Контекст выполнения
В
call
можно передать контекст выполнения:
yield call([obj, obj.method], arg1, arg2, ...)
Apply
Кроме
call
есть еще эффект apply
, который принимает аргументы в виде массива:
yield apply(obj, obj.method, [arg1, arg2, ...])
call
и apply
удобно использовать для функций, которые возвращают промис. Если нужно вызвать функцию в node-стиле, которая принимает последним параметром коллбэк, есть специальный эффект cps
.#redux #управлениесостоянием #примерыкода #saga
Telegram
React Junior
Redux-saga. Структура саг и эффекты
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты…
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты…
👍3🔥1
Redux-saga. Блокирующие и неблокирующие эффекты. Call vs Fork
В redux-saga есть прекрасный эффект
Логика у него довольно простая, попробуем реализовать его своими силами с помощью уже знакомых простых эффектов
Тут мы запускаем бесконечный цикл, ждем, когда произойдет событие и вызываем обработчик для него.
Но этот код будет работать не так, как предполагается. Догадались, почему?
Эффект
- произошло событие
- запустилась сага
- выполнение
- когда
Если событие
Чтобы это починить, нужно заменить блокирующий эффект
Демонстрация разницы: https://codesandbox.io/s/redux-saga-call-vs-fork-react-junior-8yhf4l?file=/src/app/saga.js
Важно понять, что с неблокирующими эффектами мы не можем использовать, например, блок try-catch, так как выполнение неблокирующей саги происходит вне контекста родительской саги - в "конкурентном" режиме.
#redux #управлениесостоянием #примерыкода #saga
В redux-saga есть прекрасный эффект
takeEvery
, который позволяет подписаться на каждый вызов события. Логика у него довольно простая, попробуем реализовать его своими силами с помощью уже знакомых простых эффектов
take
и call
:
function* worker() {
yield delay(1000);
yield console.log('click');
}
function* watcher() {
while (true) {
yield take('CLICK');
yield call(worker);
}
}
Тут мы запускаем бесконечный цикл, ждем, когда произойдет событие и вызываем обработчик для него.
Но этот код будет работать не так, как предполагается. Догадались, почему?
Эффект
call
является блокирующим. Это значит, что когда он запускается, функция-генератор приостанавливает свою работу, ждет, когда он выполнится, и только потом возобновляет свое выполнение. - произошло событие
CLICK
- запустилась сага
worker
- выполнение
worker
занимает некоторое время, так как там тоже есть блокирующий эффект delay
- когда
worker
выполнился (минимум через 1 секунду), продолжается выполнение watcher
- цикл идет на следующий кругЕсли событие
CLICK
поступит во время выполнения саги worker
, его просто никто не заметит. То есть логика эффекта takeEvery
не повторяется.Чтобы это починить, нужно заменить блокирующий эффект
call
на неблокирующий fork
. Он создаст отдельную "ветку" для новой саги, а выполнение родительской саги сразу же продолжится.
function* watcher() {
while (true) {
yield fork('CLICK');
yield call(worker);
}
}
Демонстрация разницы: https://codesandbox.io/s/redux-saga-call-vs-fork-react-junior-8yhf4l?file=/src/app/saga.js
Важно понять, что с неблокирующими эффектами мы не можем использовать, например, блок try-catch, так как выполнение неблокирующей саги происходит вне контекста родительской саги - в "конкурентном" режиме.
#redux #управлениесостоянием #примерыкода #saga
CodeSandbox
Redux-saga. Call vs Fork. React Junior - CodeSandbox
Redux-saga. Call vs Fork. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-scripts, redux-saga
👍4