Skip to content Skip to sidebar Skip to footer

How To "yield Put" In Redux-saga Within A Callback?

Because 'yield'-statement isn't allowed within a callback, how can i use the 'put' feature of redux-saga within a callback? I'd like to have the following callback: function onDown

Solution 1:

One possible solution, as you already mentioned, is to use channels. Here is an example that should work in your case:

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

const downloadFileChannel = channel()

export function* loadFile(id) {
  ...
  const download = RNFS.downloadFile({
     ...
     // push `S_PROGRESS` action into channel on each progress event
     progress: (progress) => downloadFileChannel.put({
       type: ACTIONS.S_PROGRESS,
       progress,
     }),
  })
  ...
}

export function* watchDownloadFileChannel() {
  while (true) {
    const action = yield take(downloadFileChannel)
    yield put(action)
  }
}

The idea here is that we will push a S_PROGRESS action on the channel for each progress event that is emitted from RNFS.downloadFile.

We also have to start another saga function that is listening to each pushed action in a while loop (watchDownloadFileChannel). Everytime an action has been taken from the channel, we use the normal yield put to tell redux-saga that this action should be dispatched.

I hope this answer will help you.


Solution 2:

I got myself into a similar situation this week.

My solution was to call a dispatch inside the callback and pass the result.

I was handling file uploads so wanted to do a readAsArrayBuffer() call, initially in my saga something like this:

function* uploadImageAttempt(action) {
  const reader = new FileReader();

  reader.addEventListener('loadend', (e) => {
    const loadedImage = reader.result;
    yield put(Actions.uploadImage(loadedImage)); // this errors, yield is not allowed
  });

  reader.readAsArrayBuffer(this.refs[fieldName].files[0]);
}

How I got round this was by doing the readAsArrayBuffer() in my component, then call a connected dispatch function:

// in my file-uploader component
handleFileUpload(e, fieldName) {
  e.preventDefault();

  const reader = new FileReader();

  reader.addEventListener('loadend', (e) => {
    const loadedImage = reader.result;
    this.props.uploadFile(
      this.constructDataObject(),
      this.refs[fieldName].files[0],
      loadedImage
    );
  });

  reader.readAsArrayBuffer(this.refs[fieldName].files[0]);

}

...

const mapDispatchToProps = (dispatch) => {
  return {
    uploadFile: (data, file, loadedImage) => {
      dispatch(Actions.uploadFile(data, file, loadedImage))
    }
  }
}

Hope that helps


Solution 3:

Beside using channel as @Alex suggest, one might also consider using call from 'redux-saga/effects'. The call effect take a function or Promise.

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

// ...

yield call(download.promise);

Solution 4:

That's the most simple way:

import store from './store' // import redux store
store.dispatch(your action creator here)

Post a Comment for "How To "yield Put" In Redux-saga Within A Callback?"