Hoow to cancel currently running request with redux-saga with parameter? - redux

I want to cancel a request in redux saga upon clicking a button
The first saga here is the one fetching data from server
function* fetchData({ payload: a, b, c}) {
try {
// ...API call
action.success(data);
} catch (e) {
action.fail(e);
} finally {
if (yield cancelled()) yield put(getDataCancelled());
}
}
Then I have a button that when clicked, it will trigger the function below. The saga below is suppose to cancel the currently running request from the fetch above.
function* stopFetchData({ payload }) {
try {
const task = yield fork(fetchData);
// API Call to clean up data from server
action.cancelSuccess(data);
yield cancel(task);
} catch (e) {
action.cancelFailed(e);
}
}
export function* fetchDataWatcher() {
yield takeLatest(SET_DATA, fetchData);
}
But I'm having an error with the parameter and as soon as this is triggered the sagas stopped working since it is finding the parameters for the function. I already tried adding the parameter, and what happens is, after clicking the fetch, and cancel. The cancel triggers, but the fetch still continues.

Related

Next JS pages/api; having trouble calling influxdb client from api

I have written a handler function inside my nextjs page/api folder;
handler(req, res) {}
Am using #influxdata/influxDb-client as mentioned in the documentation. Am using
from(queryAPI.rows(query).pipe(....).subscribe(next(value)=> {results.push(value}, complete(console.log(results); res.status(200).json(results)}
Am getting all the query value, once the observable is completed. it works most of the time.
Am pushing the intermediate results in the next part of the subscriber and trying to send the results back to client in the complete part of the subscriber. I want the request handler to wait till i get all my values from influx DB query in the complete part of the subscriber and can send the value back to client..
But the issue "Handler function will not Wait till the observable is completed". Handler function returns, before the observer gets completed. Am getting error: API resolved without sending a response...
I get all the values only when the observer is completed.
I don't know how to handle the scenario.
How can I make the handler function wait until the observable is completed?
I found the solution for the same
I used new Promise() with await, added my observable inside this promise and resolved the promise on Complete of the subscribe.
Code will look like the following :
export async function handler (req, res) {
const results=[];
await new Promise((resolve, reject) => {
from((queryAPIs.rows(query))
.pipe(map(({values, tableMeta}) => tableMeta.toObject(values)))
.subscribe(
{
next(object) => {results.push(object)}
complete() => { resolve (results) }
error(err) => { reject (err) }
});
res.status(200).send(results);
}
}

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.

Update source and sink dynamically in cycle js?

I'm writing a very simple app using redux-cycle, with 3 elements:
A text field to input url
A "connect" button to connect to a websocket end point
An output panel to show incoming messages
I have written a very simple websocket driver based on this answer https://stackoverflow.com/a/42926532/842860
The wire up code is like this:
const cycleMiddleware = createCycleMiddleware();
const {makeActionDriver} = cycleMiddleware;
function main(sources) {
return {
ACTION: sources.WEBSOCKET // The websocket driver emits commands which are consumed by reducers
}
}
const store = createStore(
reducer, // Update redux state
applyMiddleware(cycleMiddleware)
);
run(main, {
WEBSOCKET: WSDriver("ws://localhost:3000/something"), // The initial connection string
ACTION: makeActionDriver()
});
The question is how to make the websocket reconnect to another end point when the connect button is clicked? Do I need to modify my websocket driver to capture the event and reconnect, or does cyclejs provide a way to dynamically update the source/sink?
I think the appropriate method would be to, indeed, update the WSDriver to take a sink$ as input. This sink$ could contain the url you want to connect to.
Your driver would look like this
function WSDriver(endpoint$) {
var activeConnection;
return endpoint$
.map(endpointUrl => xs.create({
start: listener => {
activeConnection = new WebSocket('ws://localhost:4000');
/* no change here */
},
stop: () => {
activeConnection.close();
}
}))
.flatten() // it's a stream of stream, so we need to flatten it
}
On the app side you would need to change this
run(main, {
WEBSOCKET: WSDriver(), // < no initial connection
ACTION: makeActionDriver()
});
And in your main
function main(sources) {
return {
ACTION: sources.WEBSOCKET,
WEBSOCKET: newConnection$.startWith("ws://localhost:3000/something")
}
}
Given that the newConnection$ comes from the click events on the button.
Hope it helps

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