[Redux] redux-saga 처음 실행된 함수만 실행되고 그다음에는 취소/경고

파일 다운로드를 연속으로 했을 때 두번째 액션 부터 취소하고 토스트 보여주어야 하는 상황

  • 특정 액션이 연속 해서 일어났을 때 이전 액션 취소되고 가장 마지막에 실행된 액션이 실행되는 takeLatest의 경우, 처음으로 시작된 액션이 시작되는 것이 아닌 가장 마지막에 실행된 액션이 실행되는 것이기에 맞지 않음

→ 처음 액션이 실행되고 그 후 액션이 취소 되어야함

// action types
const DOWNLOAD_FILE_START = "DOWNLOAD_FILE_START" as const;
const DOWNLOAD_FILE_SUCCESS = "DOWNLOAD_FILE_SUCCESS" as const;
const DOWNLOAD_FILE_FAILURE = "DOWNLOAD_FILE_FAILURE" as const;

//action creators
const downloadFileStart = (file:{url:string,fileName:string}) => ({
 type: DOWNLOAD_FILE_START
 payload: file
});
const downloadFileSuccess = () => ({
 type: DOWNLOAD_FILE_SUCCESS
});
const downloadFileFailure = (error:{status:number}) => ({
 type: DOWNLOAD_FILE_FAILURE,
 payload: error
});
  1. 작성해본 코드 ( 문제 발생 )

function* downloadFile({payload}:ReturnType<typeof downloadFileStart>):Generator<StricEffect,void,never>{
  const {url,fileName} = payload;
  try{
  ... //download file from url 
  yield put(downloadFileSuccess());
  }catch(error){
  yield put(downloadFileFailure(error));
 } 
}

function* monitorActionOfDownloadFileStart(){
 while(yield take(DOWNLOAD_FILE_START)){ //DOWNLOAD_FILE_START 액션이 실행되면 while문 실행
  const downloadStartTask = yield fork(downloadFile); //downloadFile함수 실행 (다운로드 시작)
  const {start,success,failure}:{start:ReturnType<typeof downloadFileStart>,success:ReturnType<typeof downloadFileSuccess>,failure:ReturnType<typeof downloadFileFailure>} = yield race(take(DOWNLOAD_FILE_START),take(DOWNLOAD_FILE_SUCCESS),take(DOWNLOAD_FILE_FAILURE));

  if(success || failure) break; //시작보다 성공이나 실패가 더 빨리 실행된다면 시작 액션은 무조건 취소가 되고, while문에서 빠져나옴 
  if(start){ // 시작이 성공이나 실패보다 더 빨리 실행된다면 성공과 실패 액션을 취소시키므로 안됌!! 성공액션의 경우에는 성공만 알려주지만 실패액션일 경우 "다운로드에 실패했습니다"라는 toast가 나와야하기 때문에 세가지 액션으로 race 사용하면 x
   yield cancle(downloadStartTask);
   yield put(toggleToast("파일 다운로드 중입니다. 잠시후에 시도해주세요."));
  }
 }
}

//항상 실행되며 모니터링 하고 있음
function* download(){
 yield fork(monitorActionOfDownloadFileStart);
}

function* saga(){
 yield all([...,call(download)])
}
  1. 작성해본 코드 ( 문제 발생 )
function* downloadFile({payload}:ReturnType<typeof downloadFileStart>):Generator<StricEffect,void,never>{
  const {url,fileName} = payload;
  try{
  ... //download file from url 
  yield put(downloadFileSuccess());
  }catch(error){
  yield put(downloadFileFailure(error));
 } 
}

function* monitorActionOfDownloadFileStart(){
 while(yield take(DOWNLOAD_FILE_START)){ //DOWNLOAD_FILE_START 액션이 실행되면 while문 실행
  const downloadStartTask = yield fork(downloadFile); //downloadFile함수 실행 (다운로드 시작)
   yield take(DOWNLOAD_FILE_START); // DOWNLOAD_FILE_START 액션이을 기다리고 있다가 또 실행됐다면
   yield cancle(downloadStartTask); // download 취소
   yield put(toggleToast("파일 다운로드 중입니다. 잠시후에 시도해주세요."));
  }
 }
}

 /*
  하지만 DOWNLOAD_FILE_START이 실행되고 성공하거나 실패를 할 때까지 DOWNLOAD_FILE_STAR     T이 실행되지 않을 때 파란색 부분까지만 실행된 상황이기 때문에 DOWNLOAD_FILE_START이 실행이   된다면 빨간색 부분이 실행되어 다운로드가 정상적으로 되지 않는 문제 발생
 */

//항상 실행되며 모니터링 하고 있음
function* download(){
 yield fork(monitorActionOfDownloadFileStart);
}

function* saga(){
 yield all([...,call(download)])
}

  1. debouncing 이용해보자
import { delay } from 'redux-saga'

function* downloadFile(paylod:{url,fileName}):Generator<StricEffect,void,never>{
  try{
  ... //download file from url 
  yield put(downloadFileSuccess());
  }catch(error){
  yield put(downloadFileFailure(error));
 } 
}

function* watchFileDownload() {
  let action;
  while (true) {
    const downloadFileAction:ReturnType<typeof downloadFileStart> = yield take(DOWNLOAD_FILE_START);
    if (action) {
      yield put(toggleToast("파일 다운로드 중입니다. 잠시후에 시도해주세요."));
      break;
    }
    action = yield fork(downloadFile,downloadFileAction.payload)
  }
}

function* saga(){
 yield all([...,call(watchFileDownload)])
}

좋은 웹페이지 즐겨찾기