Polling with redux saga
Tags: redux, react, polling, saga
Published: 2020-03-09 19:30:45

I had been working on a solution to wait for a result to be available to update the results on a UI upon a user action. We are using react and redux. Looking at the other solutions available everywhere using redux-saga, they were all using an infinite loop and using specific redux-saga provided events to halt the polling. This made me think of making use of redux store and reducers to implement polling. So, this is my attempt to solve this problem.

###What is polling?
Polling in general term means checking the status of something OR making repeated calls for a response. Eg. news ticker; twitter feeds etc.

###Redux Saga
[Redux Saga](https://redux-saga.js.org/) -- It uses an ES6 generator functions which are similar to `async`/`await` syntax.

###Let's get to the business
For this, we are going to use a dummy service which have following APIs,

```
- POST /job -- Creates a job.
- GET /:jobId/status -- Returns the status of the job
```
Note: Not going into the details on the implementation of the APIs. Our focus will be on redux-saga implementation.

####redux-saga
Let's see the implementation
```typescript
export function* createJob(action) {
  const { payload } = action;

  yield post({
    url: '/job',
    onSuccess: ON_SUCCESS,
    onError: ON_ERROR,
    data: payload,
  });
}

export function* startPollingForStatus() {
  const job = yield select(state => getJob(state));
  yield put(getStatus(job.id));
}

export function* getStatus(action) {
  const { jobId } = action;

  yield get({
    url: `/${jobId}/status`,
    onSuccess: ON_SUCCESS_GET_STATUS,
    onError: ON_ERROR,
  });
}

export function* isJobComplete() {
  const job = yield select(state => getJob(state));
  if (job.status === 'complete') {
    return yield put(jobIsComplete(job.guid));
  }

  if (['created', 'processing'].includes(job.status)) {
    yield delay(4000);
    return yield put(getStatus(job.id));
  }

  yield put({
    type: ON_ERROR,
  });
}

export function* jobIsComplete() {
  return 'success';
}

export default function* root() {
  return yield [
    takeLatest(ON_CREATE_JOB, createJob),
    takeLatest(ON_GET_STATUS, getStatus),
    takeLatest(ON_SUCCESS, startPollingForStatus),
    takeLatest(ON_SUCCESS_GET_STATUS, isJobComplete),
    takeLatest(ON_ERROR, onError),
  ];
}
```
This might look a bit too much but let's break it down.

**startPollingForStatus**
As soon as the job is created and is returned successfully, we start the polling. As a result of the creation job, we have the returned job stored in redux store and then use the job id to fetch its status.

**isJobComplete**
Upon successful return of the status, we check for different statuses: `created`, `complete` and `processing`. If the status is not `complete`, we want to continue polling. So, we make use of `delay` function provided by redux-saga. This will delay for given time (in ms) and then resume the polling. If the status is `complete`, we will halt the polling and do something else to show the user that the job is complete.

###Conclusion
This makes use of redux store and state and get us out of using an infinite loop to poll for updates. Let me know in comments if you see any issues with this approach.

Plan to add some samples for tests and github link for working solution. :)

*UPDATE*: Code can be found [here](https://github.com/vikasgarghb/redux-polling)