Execute all actions of first Saga before executing second Saga actions - redux

I have generator function in redux saga as below:
function* updateLanguage() {
// some api call;
yield put(changeSkillsList());
yield put(fetchingSkills());
}
function* changeSkillsListGenerator() {
yield put(action1());
yield put(action2());
yield put(action3());
yield put(action4());
}
function* fetchingSkillsGenerator() {
yield put(actionA());
yield put(actionB());
yield put(actionC());
yield put(actionD());
}
I want to execute actions as below: all actions of first yield put then second
action1
action2
action3
action4
actionA
actionB
actionC
actionD
But it's executing as below:
action1
actionA
action2
action3
actionB
actionC
actionD
action4
I want to dispatch all actions of first saga before moving to second.
Kindly suggest solution for this.
Any advice or suggestions are greatly appreciated

Perhaps simply make fetchingSkillsGenerator to run only after action4:
function* fetchingSkillsGenerator() {
yield take(action4.type); // this line forces saga to wait until action4 dispatched
yield put(actionA());
...
Helpfull doc link.

Related

Dispatching an action from middleware results in a strange behavior

Redux newbie here. I understand the core concept of actions, middleware, and reducers, but one of the code snippets works not as I expected. I don't think that it's a bug, but I want to know why things happened in that way.
So, here is a code:
const middlewareOne = store => next => action => {
console.log('Middleware one recived action', action.type)
switch (action.type) {
case 'A':
return next({ type: 'B' })
default:
return next(action)
}
}
const middlewareTwo = store => next => action => {
console.log('Middleware two recived action', action.type)
switch (action.type) {
case 'B':
store.dispatch({ type: 'D' })
return next({ type: 'C' })
default:
return next(action)
}
}
function reducer(state, action)
console.log('Reducer received action', action.type)
return state
}
I have actions A, B, C, and D, two middlewares, and reducer.
First middleware receives action A and produces action B by calling next() function.
Second middleware receives action B and produces action C, and, dispatches an action D.
As I understand, there is nothing wrong with dispatching actions from middleware, but the result was very surprising for me.
Here is a console output for this code
Middleware one receive action A
Middleware two receive action B
Middleware one receive action D
Middleware two receive action D
Reducer received action D
Reducer received action C
So, what I except:
As I know, next() function passes the action to the next middleware or to the reducer if there are no middlewares in the chain, but dispatch puts action to the beginning of the pipeline (all middlewares and finally, the reducer). So, with that idea in mind, I think that action C will be reduced at first (since it's already in the middlewares pipeline), and only after that middlewares start to processing action D, but the result is completely opposite. Can you please explain to me why this happens.
Best regards, Vitaly Sulimov.
I'm relatively new to redux as well, the way I understand it, these calls aren't async so they are performed in order. You dispatch D first, so that will go through the whole chain of middlewares and reducers before the call to Next happens.
so your return next({ type: 'C' }) call only happens after store.dispatch({ type: 'D' }) finished processing

redux saga before action

I have whole application depending on config data that are loaded with request from server, how can I create blocking "before" action on every action using redux saga, now my globalSaga looks like this. The help would be really appreciated
function * rootSaga () {
yield takeLatest(LOAD_ONBOARDING.REQUEST,loadOnboardingSaga)
const res = yield put.resolve(loadOnboarding())
yield console.log(res)
yield all([
fork(globalSaga),
fork(spaceSaga),
fork(profileSaga),
fork(userSaga),
fork(pagesSaga)
])
}
takeLatest is the same as forking with automatic cancellation of each subsequent fork. Check this for a descriptive example.
So in your case since loadOnboardingSaga should continuously block next calls the cheapest solution would be to put it all under loadOnboardinSaga since there is a direct succession like this:
function* initRestSaga() {
yield all([
fork(globalSaga),
fork(spaceSaga),
fork(profileSaga),
fork(userSaga),
fork(pagesSaga)
])
}
function* loadOnboardingSaga() {
//...
const res = yield put.resolve(loadOnboarding())
yield console.log(res)
yield call(initRestSaga)
}
function* rootSaga() {
yield takeLatest(LOAD_ONBOARDING.REQUEST, loadOnboardingSaga)
}
Otherwise you have to manually fork and cancel each and every side effect in between takeLatest and last fork.

How to make sure that one Saga completes before the other?

I'm a noob in redux-sagas, so please bear with me if I miss out something obvious!
I have a scenario wherein I'm using redux-saga to fetch an access_token from an OIDC provider and store it in the browser's localStorage.
I also use Sagas to fetch some data from an API endpoint.
But I've been experiencing problems with this approach because the Saga calling the external API gets invoked before the Auth Saga can resolve with an access_token.
My Auth Saga :
export function * handleFetchTokens () {
try {
const token = yield cps(getToken)
localStorage.setItem('token', token)
const isAuthenticated = !!(token)
yield put(actions.checkAuthSuccess(isAuthenticated))
} catch (e) {
yield put(actions.checkAuthFailure(e))
}
}
export default function * sagas () {
yield fork(takeLatest, actions.CHECK_AUTH, handleFetchTokens)
}
My API Saga :
export function * handleFetchItems () {
try {
const response = yield call(getItems)
yield put(actions.fetchItemsSuccess(response))
} catch (e) {
yield put(actions.fetchItemsFailure(e.errors))
}
}
export default function * sagas () {
yield fork(takeLatest, actions.FETCH_ITEMS, handleFetchItems)
}
My root Saga :
export default function * root () {
yield fork(items.sagas)
yield fork(authentication.sagas)
}
What should be the proper way of overcoming this problem?
Personally, I'd make sure the token is received before allowing any part of my app to actually call the FETCH_ITEMS action. Assuming you don't want to introduce such logic you will have to decide what to do with FETCH_ITEMS actions before you get the token.
The easiest approach would be to just ignore them, but that also probably isn't the most feasible way to go.
So what remains is to buffer the FETCH_ITEMS actions. You can do this using actionChannel. Since you are using takeLatest you will also want to define a sliding buffer of size 1.
It could look ruffly like this:
export default function * sagas () {
const chan = yield actionChannel(actions.FETCH_ITEMS, buffers.sliding(1))
yield take('TOKEN_RECEIVED') // wait for action informing us token was received
chan.close()
yield fork(takeLatest, chan, handleFetchItems)
yield fork(takeLatest, actions.FETCH_ITEMS, handleFetchItems)
}
More about actionChannels here https://redux-saga.js.org/docs/advanced/Channels.html
Another approach - with a bit less writing but also a bit less control - instead of buffering is to wait in the fetching saga itself:
export function * handleFetchItems () {
while (!token) yield take('TOKEN_RECEIVED');
...
}
Either way, both ways rely on waiting for a TOKEN_RECEIVED action you need to dispatch once the token is received.

redux-saga: tracking multiple async tasks

I am using sagas to track multiple async tasks, but there is one problem I haven't been able to completely solve:
function* performTask1() {
// Some logic here to takeLatest for the relevant component
// check if component id matches?
// Only perform API call with the latest
const { result } = yield takeLatest('doAsync2')
}
function* performTask2() {
const { result } = yield call(api, args)
// do something with results (not relevant)
}
function* watchAsyncTasks() {
yield takeEvery('doAsync2', performTask2)
yield takeEvery('doAsync1', performTask1)
}
componentA dispatches doAsync1
componentB dispatches doAsync1
component C dispatches doAsync2 (for good measure)
componentA dispatches doAsync1
componentB dispatches doAsync1
How can I use sagas to ensure that only sagas 3, 4, and 5 complete their API call?
function* generator(){
yield call(api,params);
yield call(api2, params2);
}
const gen = generator;
gen.next() // done: false/true
gen.next() // done: false/true

Process queue with redux-saga

I am trying to implement a queue handler for managing notifications with a redux-saga generator.
Basically, I need to show notifications sequentually as they enter the queue.
For this, I have a queue array in the redux store, an action QUQUE_NOTIFICATION action to add to queue and SHOW_NOTIFICATION to remove a notification for queue.
My current saga implementation is that simple :
export function* watchQueue() {
while (true) {
const state = yield select()
const queue = state.queue
if (queue.length > 0) {
yield put({ action: 'SHOW_NOTIFICATION', queue[0])
}
yield call(delay, 5000);
}
}
}
The problem with current implementation is that when a queue is empty a QUQUE_NOTIFICATION is dispatched generator can be waiting for the delay to finish.However, I want to show the first notification as soon as it enters the queue. Any ideas?
I've had the same idea for showing up notification (queueing them) however saga provides already implemented solution in terms of channels.
I have:
export function * notificationSaga () {
const requestChan = yield actionChannel(Notification.request)
while (true) {
const { payload } = yield take(requestChan)
yield call(showNotification, payload)
}
}
which I believe is elegant solution to your problem.
showNotification is another function which actually shows notifications and waits a bit before taking it down.

Resources