web-dev-qa-db-ja.com

コールバック内でredux-sagaに "yield put"する方法は?

「yield」ステートメントはコールバック内では許可されないため、コールバック内でredux-sagaの「put」機能を使用するにはどうすればよいですか?

次のコールバックを希望します。

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

yieldは単純な関数では使用できないため、これは機能せず、「予期しないトークン」になります。そうしないと、コールバックを "function *"として渡すことができません。 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;

}
18
delete

すでに述べたように、1つの可能な解決策は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)
  }
}

ここでの考え方は、S_PROGRESSから発行される進行状況イベントごとに、チャネルでRNFS.downloadFileアクションをプッシュすることです。

また、whileループ(watchDownloadFileChannel)でプッシュされた各アクションをリッスンする別のsaga関数を開始する必要があります。チャネルからアクションが実行されるたびに、通常のyield putを使用して、このアクションをディスパッチする必要があることをredux-sagaに通知します。

この回答がお役に立てば幸いです。

29
Alex

今週も同じような状況に陥りました。

私の解決策は、コールバック内でディスパッチを呼び出し、結果を渡すことでした。

私はファイルのアップロードを処理していたので、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))
    }
  }
}
_

それが役に立てば幸い

1
jolyonruss

@Alexが示唆するようにchannelを使用する以外に、'redux-saga/effects'callの使用を検討することもできます。 call効果は関数またはPromiseを受け取ります。

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

// ...

yield call(download.promise);
0