web-dev-qa-db-fra.com

Comment "céder" dans redux-saga dans un callback?

Etant donné que l'instruction "yield" n'est pas autorisée dans un rappel, comment puis-je utiliser la fonctionnalité "put" de redux-saga dans un rappel?

J'aimerais avoir le rappel suivant:

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

Cela ne fonctionne pas et aboutit à un "jeton inattendu", car yield n'est pas autorisé dans une fonction simple. Sinon, je ne peux pas passer de callback en tant que " fonction * ", ce qui permettrait d'obtenir un rendement. ES6 semble cassé ici.

J'ai lu que Redux-saga fournit des fonctionnalités appelées " channels ", mais pour être honnête, je ne les ai pas comprises. J'ai lu plusieurs fois à propos de ces canaux et de l'exemple de code, mais dans tous les exemples, ils ont résolu des problèmes très difficiles et différents, ce n'est pas mon cas simple et, au bout du compte, je suis arrivé là-haut.

Quelqu'un peut-il me dire une solution pour résoudre ce problème?

Le contexte entier:

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;

}
13
delete

Comme vous l'avez déjà mentionné, l'une des solutions possibles consiste à utiliser channels. Voici un exemple qui devrait fonctionner dans votre cas:

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

L'idée ici est que nous allons pousser une action S_PROGRESS sur le canal pour chaque événement de progression émis par RNFS.downloadFile.

Nous devons également démarrer une autre fonction de saga qui écoute chaque action poussée dans une boucle while (watchDownloadFileChannel). Chaque fois qu'une action a été prise depuis le canal, nous utilisons le yield put normal pour indiquer à redux-saga que cette action doit être envoyée.

J'espère que cette réponse vous aidera.

19
Alex

Je me suis retrouvé dans une situation similaire cette semaine.

Ma solution était d'appeler une dépêche à l'intérieur du rappel et de transmettre le résultat.

Je m'occupais des envois de fichiers, je voulais donc faire un appel readAsArrayBuffer(), d'abord dans ma saga, quelque chose comme ceci:

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

Comment j'ai pu résoudre ce problème en faisant la readAsArrayBuffer() dans mon composant, puis en appelant une fonction de dispatch connectée:

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

J'espère que cela pourra aider

1
jolyonruss

En plus d'utiliser channel comme le suggère @Alex, on pourrait également envisager d'utiliser call à partir de 'redux-saga/effects'. L'effet call prend une fonction ou Promise.

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

// ...

yield call(download.promise);

0
Thomas - BeeDesk