Redux updateElementSaga has been canceled. What for?
I just realized the drag function with response-dnd, and when the user dropped the item SkyElement
in my application, I update top
and left
the server, which in turn updates the reductions storage
However, the update call works sometimes, not every time. And in my console I see a warning;updateElementSaga has been cancelled
In my SlotView.js
in function I have:
this.props.dispatch(requestUpdateElement({ id, top, left }));
In my elements/actions.js
:
export function requestUpdateElement(element) {
return { type: 'requestUpdateElement', element };
}
In my elements/sagas.js
:
export function *updateElementSaga(action) {
const response = yield call(api.updateElement, action.element);
if (response.element) {
// debugger; // this hits, saga was cancelled will have appeared in the console at this point
yield put(actions.receiveElement(response.element));
} else if (response.error) {
console.log('error receiving element');
}
}
export default [
takeLatest('requestUpdateElement', updateElementSaga),
];
In api.js
:
export function updateElement(element) {
const userId = JSON.parse(localStorage.cookies).userId;
element.userId = userId;
if (userId) {
return apiHelper.put(
`${apiHelper.getBaseUrl()}/users/${element.userId}/elements/${element.id}`,
{element},
{headers: apiHelper.getHeaders()}
).catch((error) => {
return {error};
});
} else {
console.log('user ID could not be found for request');
}
}
And mine elements/reducer.js
:
const defaultState = {
elementsMap: {},
visibleElements: [],
unplacedElements: [],
};
export default function(state = defaultState, action) {
switch (action.type) {
case 'receiveElement':
let element = null;
let unplacedElement = null;
if (action.element.sectorId === undefined) {
unplacedElement = `${action.element.id}`;
} else {
element = `${action.element.id}`;
// don't add, duplicate
const newState = {...state}; // copy old state
delete newState[`${action.element.id}`]; // delete the item from the object
const newVisibleElements = newState.visibleElements.filter(e => e !== `${action.element.id}`); // remove item from visible elements
const newUnplacedElements = newState.unplacedElements.filter(e => e !== `${action.element.id}`);
return {
...newState,
elementsMap: {
...newState.elementsMap,
[element]: action.element,
},
visibleElements: [...newVisibleElements, element],
unplacedElements: [...newUnplacedElements],
};
}
return {
...state,
elementsMap: {
...state.elementsMap,
[action.element.id]: action.element,
},
visibleElements: [...state.visibleElements, element],
unplacedElements: [...state.unplacedElements, unplacedElement],
};
default:
return state;
}
}
As I mentioned earlier, sometimes the update works, but not every time. I've narrowed it down to the client. The server seems to be doing fine. Any idea what I am doing wrong here? Thank!
source to share
If you are using takeLatest
, the documentation on the reduction saga mentions:
https://redux-saga.js.org/docs/basics/UsingSagaHelpers.html
Unlike takeEvery, takeLatest allows only one fetchData task to run at any given time. And this will be the last task launched. If the previous task is still running while another fetchData task is running, the previous task will be automatically canceled .
Where fetchData
is the generator function which is supplied with takeLatest
ortakeEvery
And when your UI keeps referencing the same action, before it completes, it will continue to undo the last invoked action, and hence you will keep getting the message intermittently:
updateElementSaga has been cancelled
Which is by nature takeLatest
doing the right thing. What is:
Always take the latest invoked action
If you want every action to be caught and processed, use takeEvery
like:
export default [
takeEvery('requestUpdateElement', updateElementSaga),
];
source to share