如何在回调函数中使用redux-saga中的“yield put”?

32

由于在回调函数中不允许使用"yield"语句,那么我该如何在回调函数中使用redux-saga的"put"功能呢?

我想要以下的回调函数:

function onDownloadFileProgress(progress) {
  yield put({type: ACTIONS.S_PROGRESS, progress})
}

由于yield在普通函数中不允许,所以出现了"意外令牌"错误。否则我无法将回调函数作为"function *"进行传递,这将允许使用yield。ES6在这里似乎出现了问题。

我读到redux-saga提供了一些名为"channels"的功能,但老实说,我还没有理解它们。我已经多次阅读过有关这些通道的说明和示例代码,但在所有示例中,他们都解决了非常困难和不同的问题,而不是我的简单情况,最后我只是停滞不前。

有人可以告诉我如何处理这个问题吗?

整个上下文:

function onDownloadFileProgress(progress) {
  yield put({type: ACTIONS.S_PROGRESS, progress})
}

export function * loadFile(id) {
  let url = `media/files/${id}`;

  const tempFilename = RNFS.CachesDirectoryPath + '/' + id;

  const download = RNFS.downloadFile( {
    fromUrl: url,          
    toFile: tempFilename,  
    background: false,
    progressDivider: 10,
    progress: onDownloadFileProgress,
  })

  yield download.promise;

}

不允许 <-> 你不能 - Bergi
你想表达什么意思?这有什么区别吗? - delete
4个回答

61

正如您已经提到的,一种可能的解决方案是使用通道(channels)。这里是一个在您的情况下应该可行的示例:

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)
  }
}

这里的想法是,每当从RNFS.downloadFile发出进度事件时,我们将在通道上推送一个 S_PROGRESS 操作。

我们还必须启动另一个监听每个推送操作的 saga 函数,在 while 循环 (watchDownloadFileChannel) 中进行。每次从通道中获取一个操作后,我们使用正常的 yield put 来告诉 redux-saga 应该分派此操作。

我希望这个答案能帮到你。


非常好用!非常感谢你! - delete
4
这看起来像是适用于我类似问题的解决方案,但我遇到了通道缓冲区溢出的问题。你是如何将 watchDownloadFileChannelsagaMiddleware 进行连接的? - dougajmcdonald
谢谢!这帮助我以更清晰的方式将socket.io与redux-saga配合使用。 - Benny Schmidt
你如何将 watchDownloadFileChannel 连接到 saga 中间件? - Matteo
6
好的,对于那些想知道如何连接 watchDownloadChannel 的人,你可以简单地将这段代码 takeEvery(channel, watchDownloadChannel) 导出并导入到 rootSaga 中。 - Matteo

2
这是最简单的方法:
import store from './store' // import redux store
store.dispatch(your action creator here)

1
除了像 @Alex 建议的使用 channel 之外,还可以考虑使用来自 'redux-saga/effects'callcall effect 接受一个函数或 Promise

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

// ...

yield call(download.promise);


1

这个星期我遇到了类似的情况。

我的解决方法是在回调函数中调用派发函数并传递结果。

我正在处理文件上传,所以想要在saga中使用readAsArrayBuffer()调用,最初的代码如下:

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]);
}

我是一名有用的助手,可以为您翻译文本。
我是如何解决这个问题的:在我的组件中使用readAsArrayBuffer(),然后调用连接的分派函数。
// 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))
    }
  }
}

希望这有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接