Expected behavior: requests are made to the api with an interval of 1.5 seconds until a successful request is completed, or the sendMessageCancel action is called with the required id.
What’s available: after invoking the action, the state changes, but requests continue to go with an interval of 1.5 seconds
P.S. logs after race execution are also not called
Sagas:
import {takeEvery, delay, call, put, race, take} from 'redux-saga / effects';
import axios, {AxiosResponse} from 'axios';
import {sendMessageStart, sendMessageSuccess, sendMessageProgress, sendMessageCancel, ISendMessage} from './slice';
import sendMessageAPI from '../../utils/api/sendMessageAPI';
import {ISendMessageResponse} from '../../interfaces/Responses/chat/ISendMessageResponse';
function * sendMessageSaga ({payload}: ReturnType & lt; typeof sendMessageStart & gt;) {
const {token, cancel} = axios.CancelToken.source ();
while (true) {
try {
let {action, api} = yield race ({
action: take (action = & gt; {
return action.type == sendMessageCancel.type & amp; & amp; action.payload == payload._id
}),
api: call (sendMessageAPI.send, payload, token, function * (progress: number) {
yield put (sendMessageProgress ({message: payload._id, progress}));
})
});
const resp = api as AxiosResponse & lt; ISendMessageResponse & gt ;;
console.log (resp);
console.log (action);
if (resp)
yield put (sendMessageSuccess (payload._id));
else {
console.log ('Cancel');
return cancel ();
}
break;
} catch (e) {
console.log (e);
yield delay (1500);
}
}
}
export default function * sendMessageWatchSaga () {
yield takeEvery (sendMessageStart.type, sendMessageSaga);
}
API
import axios, {CancelToken, CancelTokenSource} from 'axios';
import {ISendMessage} from '../../redux/sendMessage/slice';
import {ISendMessageResponse} from '../../interfaces/Responses/chat/ISendMessageResponse';
const client = axios.create ({
baseURL: process.env.API_URL || 'http: // localhost: 5000'
});
const sendMessageAPI = {
async send (message: ISendMessage, cancelToken: CancelToken, callback: (progress: number) = & gt; void) {
return client.post & lt; ISendMessageResponse & gt; ('/ messages', message, {
headers: {Authorization: `Bearer $ {localStorage.getItem ('token')}`},
cancelToken: cancelToken,
onUploadProgress: progressEvent = & gt; {
callback (progressEvent.loaded / progressEvent.total);
}
});
}
};
export default sendMessageAPI;
Slice
import {createSlice, PayloadAction} from '@ reduxjs / toolkit';
import {RootState} from '../index';
import {IMessage} from '../../interfaces/IMessage';
// create initial state
export type ISendMessage = {file ?: File} & amp; IMessage
type ISendMessageItem = {
progress: number,
msg: ISendMessage
};
type ISendMessageState = Record & lt; string, ISendMessageItem & gt ;;
const initialState: ISendMessageState = {};
// create slice
const sendMessageSlice = createSlice ({
name: 'sendMessage',
initialState,
reducers: {
start (state, action: PayloadAction & lt; ISendMessage & gt;) {
state [action.payload._id] = {
progress: 0,
msg: action.payload
};
},
success (state, action: PayloadAction & lt; string & gt;) {
delete state [action.payload];
},
progress (state, action: PayloadAction & lt; {message: string, progress: number} & gt;) {
state [action.payload.message] .progress = action.payload.progress;
},
cancel (state, action: PayloadAction & lt; string & gt;) {
delete state [action.payload];
}
}
});
// selectors
Export Const SelectsendMessageState = (State: RootState) = & gt; state.sendMessage;
Export Const SelectsendMessage = (ID: String) = & gt; (State: rootstate) = & gt; SelectsendMessageState (State) [ID];
Export Const SelectSendMessagesFordialog = (ID: String) = & gt;
(State: rootstate) = & gt; Object.Values โโ(State). Filter (IT = & gt; it.msg.dialog._id == id);
// Exports.
EXPORT CONST {
Start: SendMessagestart, Success: SendMessageSuccess,
Progress: SendMessageProgress, Cancel: SendMessageCancel
} = SendMessagesLice.Actions;
Export Default SendMessagesLice.Reducer;
Answer 1
The problem was banal, the call of the API and the subsequent delay occupied the flow of saga, and she simply ignored the Calling of the SendMessageCancel action, so I used fork and everything earned
import {takeevery, delay, call, put, fork, take, Cancel, Cancelled, Race, SELECT} from 'Redux- SAGA / EFFECTS ';
Import Axios, {axiosreSponse, CancelToken} from 'Axios';
Import {iSendMessageresponse} from '../../interfaces/responses/chat/isendMessageresponse';
Import {SendMessageStart, SendMessageSuccess, SendMessageProgress, SendMessageCancel, iSendMessage} from './slice';
Import {MessagesAdd} from '../messages';
Import {dialogsadd} from '../dialogs';
import {chatmessagesadd} from '../chat/messages/slice';
Import SendMessageApi from '../../utils/api/sendMessageApi';
Import {selectchatdialogstate} from '../chat/dialog/slice';
Function * SendMessageApi (Data: ISendMessage, Token: CancelToken) {
Let i = 0;
While (True) {
Try {
// Make API Call
Const Resp: AXIOSRESPONSE & LT; ISendMessageResponse & GT; = Yield Call (SendMessageApi.send,
Data, Token, Function * (PROGRESS: NUMBER) {
Yield Put (SendMessageProgress ({Message: Data._ID, Progress}));
});
// Get Current Dialog
const {dialog} = Yield Select (SelectChatdialOGstate);
// Update State
Yield Put (SendMessageSuccess (Data._ID));
Yield Put (MessagesAdd ({
... resp.data.newmessage,
Author: resp.data.newmessage.Author._id
} As Any));
// Update Dialog Last Message
Yield PUT (Dialogsadd ({_ ID: Data.dialog._ID, LastMessage: resp.data.newmessage._id} As Any));
if (dialog == data.dialog._id)
Yield Put (ChatmessagesAdd ({
Message: resp.data.newmessage._ID,
FIRST: True.
}));
// Exit Loop.
Break;
} Catch (E) {
// EXIT ON TASK CANCEL
If (Yield Cancelled ())
Return;
// Increase Interval.
IF (I & LT; 60)
I ++;
Yield Delay (1000 * i);
}
}
}
Function * SendMessagesAga ({Payload}: ReturntType & lt; Typeof SendMessagestart & gt;) {
Const Source = axios.cancelToken.source (),
// Start Send Messages
Task = Yield Fork (SendMessageApi, Payload, Source.token);
// Start Race Between Success Sending and Cancellation
Const {API, CancelMessage} = Yield Race ({
API: Take (Action = & gt; action.type == SendMessageSuccess.Type & amp; & amp; action.payload == payload._id),
CancelMessage: Take (Action = & GT; Action.Type == SendMessagecancel.Type & amp; & amp; action.payload == payload._id)
});
If (CancelMessage) {
// Stop API Calls
Yield Cancel (Task);
source.cancel ();
}
}
Export Default Function * SendMessageWatchsaga () {
Yield TakeEvery (SendMessagestart.Type, SendMessagesAga);
}