Search code examples
reactjsreduxredux-saga

Redux saga, axios and progress event


Is there clean/short/right way to using together axios promise and uploading progress event?

Suppose I have next upload function:

function upload(payload, onProgress) {
  const url = '/sources/upload';

  const data = new FormData();

  data.append('source', payload.file, payload.file.name);

  const config = {
    onUploadProgress: onProgress,
    withCredentials: true
  };

  return axios.post(url, data, config);
}

This function returned the promise.

Also I have a saga:

function* uploadSaga(action) {
  try {
    const response = yield call(upload, payload, [?? anyProgressFunction ??]);
    yield put({ type: UPLOADING_SUCCESS, payload: response });
  } catch (err) {
    yield put({ type: UPLOADING_FAIL, payload: err });
  }
}

I want to receive progress events and put it by saga. Also I want to catch success (or failed) result of the axios request. Is it possible?

Thanks.


Solution

  • So I found the answer, thanks Mateusz Burzyński for the clarification.

    We need use eventChannel, but a bit canningly.

    Suppose we have api function for uploading file:

    function upload(payload, onProgress) {
      const url = '/sources/upload';
    
      const data = new FormData();
    
      data.append('source', payload.file, payload.file.name);
    
      const config = {
        onUploadProgress: onProgress,
        withCredentials: true
      };
    
      return axios.post(url, data, config);
    }
    

    In saga we need to create eventChannel but put emit outside.

    function createUploader(payload) {
    
      let emit;
      const chan = eventEmitter(emitter => {
    
        emit = emitter;
        return () => {}; // it's necessarily. event channel should 
                         // return unsubscribe function. In our case 
                         // it's empty function
      });
    
      const uploadPromise = upload(payload, (event) => {
        if (event.loaded.total === 1) {
          emit(END);
        }
    
        emit(event.loaded.total);
      });
    
      return [ uploadPromise, chan ];
    }
    
    function* watchOnProgress(chan) {
      while (true) {
        const data = yield take(chan);
        yield put({ type: 'PROGRESS', payload: data });
      }
    }
    
    function* uploadSource(action) {
      const [ uploadPromise, chan ] = createUploader(action.payload);
      yield fork(watchOnProgress, chan);
    
      try {
        const result = yield call(() => uploadPromise);
        put({ type: 'SUCCESS', payload: result });
      } catch (err) {
        put({ type: 'ERROR', payload: err });
      }
    }