web-dev-qa-db-fra.com

Puis-je envoyer une action en réducteur?

est-il possible d'envoyer une action dans un réducteur lui-même? J'ai une barre de progression et un élément audio. L'objectif est de mettre à jour la barre de progression lorsque l'heure est mise à jour dans l'élément audio. Mais je ne sais pas où placer le gestionnaire d'événements ontimeupdate ni comment envoyer une action dans le rappel de ontimeupdate pour mettre à jour la barre de progression. Voici mon code:

//reducer

const initialState = {
    audioElement: new AudioElement('test.mp3'),
    progress: 0.0
}

initialState.audioElement.audio.ontimeupdate = () => {
    console.log('progress', initialState.audioElement.currentTime/initialState.audioElement.duration);
    //how to dispatch 'SET_PROGRESS_VALUE' now?
};


const audio = (state=initialState, action) => {
    switch(action.type){
        case 'SET_PROGRESS_VALUE':
            return Object.assign({}, state, {progress: action.progress});
        default: return state;
    }

}

export default audio;
160
klanm

Envoyer une action dans un réducteur est un anti-motif. Votre réducteur devrait être sans effets secondaires, simplement digérer la charge d'action et renvoyer un nouvel objet d'état. L'ajout d'écouteurs et la répartition des actions dans le réducteur peuvent entraîner des actions chaînées et d'autres effets secondaires.

Cela ressemble à votre classe AudioElement initialisée et à l'écouteur d'événements qui appartiennent à un composant plutôt qu'à un état. Dans l'écouteur d'événements, vous pouvez envoyer une action qui mettra à jour l'état progress.

Vous pouvez initialiser l'objet AudioElement class dans un nouveau composant React ou simplement convertir cette classe en un composant React.

class MyAudioPlayer extends React.Component {
  constructor(props) {
    super(props);

    this.player = new AudioElement('test.mp3');

    this.player.audio.ontimeupdate = this.updateProgress;
  }

  updateProgress () {
    // Dispatch action to reducer with updated progress.
    // You might want to actually send the current time and do the
    // calculation from within the reducer.
    this.props.updateProgress();
  }

  render () {
    // Render the audio player controls, progress bar, whatever else
    return <p>Progress: {this.props.progress}</p>;
  }
}

class MyContainer extends React.Component {
   render() {
     return <MyAudioPlayer updateProgress={this.props.updateProgress} />
   }
}

function mapStateToProps (state) { return {}; }

return connect(mapStateToProps, {
  updateProgressAction
})(MyContainer);

Notez que la updateProgressAction est automatiquement encapsulée avec dispatch, vous n'avez donc pas besoin d'appeler le dispatch directement.

122
mtaube

Le début d'une autre dépêche avant la fin de votre réducteur est un anti-motif, car l'état que vous avez reçu au début de votre réducteur ne sera plus l'état actuel de l'application à la fin de votre réducteur. Mais programmer une autre dépêche à l'intérieur d'un réducteur n'est PAS un anti-modèle. En fait, c’est ce que fait le langage Elm et, comme vous le savez, Redux est une tentative de transformation de l’architecture Elm en JavaScript.

Voici un middleware qui ajoutera la propriété asyncDispatch à toutes vos actions. Lorsque votre réducteur a terminé et qu'il a renvoyé le nouvel état de l'application, asyncDispatch déclenchera store.dispatch avec l'action que vous lui avez donnée.

// This middleware will just add the property "async dispatch"
// to actions with the "async" propperty set to true
const asyncDispatchMiddleware = store => next => action => {
  let syncActivityFinished = false;
  let actionQueue = [];

  function flushQueue() {
    actionQueue.forEach(a => store.dispatch(a)); // flush queue
    actionQueue = [];
  }

  function asyncDispatch(asyncAction) {
    actionQueue = actionQueue.concat([asyncAction]);

    if (syncActivityFinished) {
      flushQueue();
    }
  }

  const actionWithAsyncDispatch =
    Object.assign({}, action, { asyncDispatch });

  const res = next(actionWithAsyncDispatch);

  syncActivityFinished = true;
  flushQueue();

  return res;
};

Maintenant votre réducteur peut faire ceci:

function reducer(state, action) {
  switch (action.type) {
    case "fetch-start":
      fetch('wwww.example.com')
        .then(r => r.json())
        .then(r => action.asyncDispatch({ type: "fetch-response", value: r }))
      return state;

    case "fetch-response":
      return Object.assign({}, state, { whatever: action.value });;
  }
}
120
Marcelo Lazaroni

Vous pouvez essayer d’utiliser une bibliothèque telle que redux-saga . Il permet un moyen propre très de séquencer des fonctions asynchrones, de déclencher des actions, d'utiliser des délais, etc. C'est très puissant!

10
chandlervdw

redux-loop prend exemple sur Elm et fournit ce modèle.

3
Quang Van