简体   繁体   中英

retry functionality in redux-saga

in my application I have the following code

componentWillUpdate(nextProps) {
  if(nextProps.posts.request.status === 'failed') {
    let timer = null;

    timer = setTimeout(() => {
      if(this.props.posts.request.timeOut == 1) {
        clearTimeout(timer);
        this.props.fetchData({
          page: this.props.posts.request.page
        });
      } else {
        this.props.decreaseTimeOut();
      }
    }, 1000);
  }
}

What it does is that, when an API request encountered an error maybe because there is no internet connection (like how facebook's chat works), or there was an error in the back-end, it would retry after five seconds, but the setTimeout needs to be set every one second to update a part of the store, ie, the line this.props.decreaseTimeOut(); , but if the counter has run out, so five seconds have passed, the if block would run and re-dispatch the fetchData action .

It works well and I have no problem with it, at least in terms of functionality, but in terms of code design, I know that it's a side-effect and it should not be handled in my react-component, and since I'm using redux-saga (but I'm new to redux-saga, I just learned it today), I want to transform that functionality into a saga, I don't quite have an idea as to how to do that yet, and here is my fetchData saga by the way.

import {
  take,
  call,
  put
} from 'redux-saga/effects';

import axios from 'axios';

export default function* fetchData() {
  while(true) {
    try {
      let action = yield take('FETCH_DATA_START');
      let response = yield call(axios.get, '/posts/' + action.payload.page);
      yield put({ type: 'FETCH_DATA_SUCCESS', items: [...response.data.items] });
    } catch(err) {
      yield put({ type: 'FETCH_DATA_FAILED', timeOut: 5 });
    }
  }
}

The less intrusive thing for your code is using the delay promise from redux-saga:

catch(err) {
   yield put({ type: 'FETCH_DATA_FAILED'});

   for (let i = 0; i < 5; i++) {
       yield call(delay, 1000);
       yield put(/*Action for the timeout/*);
  }
}

But I'd refactor your code in this way:

function* fetchData(action) {
    try {
      let response = yield call(axios.get, '/posts/' + action.payload.page);
      yield put({ type: 'FETCH_DATA_SUCCESS', items:[...response.data.items] });
    } catch(err) {
      yield put({ type: 'FETCH_DATA_FAILED'});
      yield put({ type: 'SET_TIMEOUT_SAGA', time: 5 });
    }
  }
}

function *setTimeoutsaga(action) {
   yield put({type: 'SET_STATE_TIMEOUT', time: action.time}); // Action that update your state
   yield call(delay, 1000);

   // Here you use a selector which take the value if is disconnected:
   // https://redux-saga.js.org/docs/api/#selectselector-args
   const isStillDisconnected = select() 
   if (isStillDisconnected) {
       yield put({type: 'SET_TIMEOUT_SAGA', time: action.time - 1});
}

function *fetchDataWatchers() {
    yield takeEvery('FETCH_DATA_START', fetchData);
    yield takeEvery('SET_TIMEOUT_SAGA', setTimeoutSaga);

    // You can insert here as many watcher you want
}

export default [fetchDataWatchers]; // You will use run saga for registering this collection of watchers

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM