简体   繁体   English

如何在回调中在 redux-saga 中“产出放置”?

[英]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?因为回调中不允许使用“yield”语句,我如何在回调中使用 redux-saga 的“放置”功能?

I'd like to have the following callback:我想要以下回调:

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

This isn't working and ends up in "unexpected token", because yield is not allowed in a plain function.这不起作用并以“意外标记”结束,因为在普通函数中不允许yield Otherwise i can't pass a callback as a " function * ", that would allow yield.否则我无法将回调作为“函数 * ”传递,这将允许屈服。 ES6 seems broken here. ES6 在这里似乎坏了。

I've read that redux-saga provides some features called " channels ", but to be honest, i didn't get it.我读过 redux-saga 提供了一些称为“通道”的功能,但老实说,我没有理解。 I've read several times about these channels and example code, but in all examples they've solved very difficult and different problems, not my simple case and at the end of the day i've got up there.我已经多次阅读这些渠道和示例代码,但在所有示例中,它们都解决了非常困难和不同的问题,而不是我的简单案例,并且在一天结束时我已经解决了。

Can someone tell me a solution how to deal with this issue?有人可以告诉我如何处理这个问题的解决方案吗?

The whole context:整个上下文:

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;

}

One possible solution, as you already mentioned, is to use channels .正如您已经提到的,一种可能的解决方案是使用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 .这里的想法是我们将在通道上为从RNFS.downloadFile发出的每个进度事件推送一个S_PROGRESS操作。

We also have to start another saga function that is listening to each pushed action in a while loop ( watchDownloadFileChannel ).我们还必须启动另一个 saga 函数来监听while循环中的每个推送动作( 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.每次从通道中执行一个动作时,我们都会使用正常的yield put来告诉redux-saga这个动作应该被分派。

I hope this answer will help you.我希望这个答案对你有帮助。

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:我正在处理文件上传,所以想做一个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]);
}

How I got round this was by doing the readAsArrayBuffer() in my component, then call a connected dispatch function:我如何解决这个问题是通过在我的组件中执行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))
    }
  }
}

Hope that helps希望有帮助

Beside using channel as @Alex suggest, one might also consider using call from 'redux-saga/effects' .除了像@Alex 建议的那样使用channel之外,还可以考虑使用来自'redux-saga/effects' call The call effect take a function or Promise . call效果采用函数或Promise

 import { call } from 'redux-saga/effects'; // ... yield call(download.promise);

That's the most simple way:这是最简单的方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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