If you got your hands dirty in Redux world, you may have come across a situation where you want to pass some Redux effect to outside of Redux as a callback. For example let's think about yield put.
saga.ts
const props = {
// ...
fetchSuccessCallback: () => {
yield put({
actionType,
actionData
})
}
}
yield call(openSomeModal, {
...props
});
You want to trigger that fetchSuccessCallback function in the modal. Yes you may think that why don't you use useDispatch in the modal and make life easier.
Yes, you are on the right track but in this scenario, this modal is outside of redux context provider so redux hooks are unavailable.
So, back to above example, if we try to run, it does not working and ends up in "unexpected token", because yield is not allowed in a plain function.
One possible solution is using channels. Redux channels generalize Redux Effects to communicate with external event sources or between Sagas themselves.
Take a look at below code.
saga.ts
import {
channel
} from 'redux-saga';
// ...
const fetchChannel = channel();
// ..
const props = {
// ...
fetchSuccessCallback: fetchedData => fetchChannel.put({
fetchedData
})
}
yield call(openSomeModal, {
...props
});
// ...
export function* watchFetchChannel() {
while (true) {
const {
fetchedData
} = yield take(fetchChannel);
yield put(actions.fetchSuccess(fetchedData));
}
}
The idea here is that we will push a fetch action on the channel for each fetch event that is emitted from fetchSuccessCallback.
We also have to start another saga function that is listening to each pushed action in a while loop (watchFetchChannel). Every time an action has been taken from the channel, we use the normal yield put to tell redux-saga that this action should be dispatched.
That's it. I know it looks ugly and requires some yield and generator function gymnastics but it's what it's. If you want to stick with useDispatch, just wrap everything inside Redux provider.
Sources:
See you in the next chapter :)