Manipulation d'un minuteur dans React / Flux

je travaille sur une application où je veux un compte à rebours de, disons, 60 secondes à 0 et puis changer un peu de contenu, après quoi le compte à rebours redémarre à 60.

j'ai mis en oeuvre ceci dans React et Flux mais puisque je suis nouveau à cela, je suis encore en train de rencontrer quelques problèmes.

je veux maintenant ajouter un bouton marche/arrêt de la minuterie. Je ne sais pas où mettre/gérer l'état de minuterie.

j'ai un composant Timer.jsx qui ressemble à ceci:

var React = require('react');
var AppStore = require('../stores/app-store.js');
var AppActions = require('../actions/app-actions.js');

function getTimeLeft() {
  return {
    timeLeft: AppStore.getTimeLeft()
  }
}

var Timer = React.createClass({
  _tick: function() {
    this.setState({ timeLeft: this.state.timeLeft - 1 });
    if (this.state.timeLeft < 0) {
      AppActions.changePattern();
      clearInterval(this.interval);
    }
  },
  _onChange: function() {
    this.setState(getTimeLeft());
    this.interval = setInterval(this._tick, 1000);
  },
  getInitialState: function() {
    return getTimeLeft();
  },
  componentWillMount: function() {
    AppStore.addChangeListener(this._onChange);
  },
  componentWillUnmount: function() {
    clearInterval(this.interval);
  },
  componentDidMount: function() {
    this.interval = setInterval(this._tick, 1000);
  },
  render: function() {
    return (
      <small>
        ({ this.state.timeLeft })
      </small>
    )
  }
});

module.exports = Timer;

Il récupère un compte à rebours de la durée de la boutique, où je n'ai tout simplement:

var _timeLeft = 60;

maintenant, quand je veux implémenter un bouton start/stop, j'ai l'impression que je devrais aussi implémenter ceci par des actions de Flux, correct? Donc je pensais avoir quelque chose comme ça dans mon magasin:

dispatcherIndex: AppDispatcher.register(function(payload) {
  var action = payload.action;

  switch(action.actionType) {
    case AppConstants.START_TIMER:
      // do something
      break;
    case AppConstants.STOP_TIMER:
      // do something
      break;
    case AppConstants.CHANGE_PATTERN:
      _setPattern();
      break;
  }

  AppStore.emitChange();

  return true;
})

cependant, puisque mon composant Timer gère actuellement le setInterval, Je ne sais pas comment faire fonctionner Mes événements START/STOP_TIMER. Dois-je aller le truc de setInterval du composant de minuterie au magasin et d'une façon ou d'une autre transmettre ça à mon composant?

le code Complet peut être trouvé ici.

16
demandé sur cabaret 2014-12-22 17:15:54

3 réponses

j'ai fini par télécharger votre code et implémenter la fonctionnalité start/stop/reset que vous vouliez. Je pense que c'est probablement la meilleure façon d'expliquer les choses à montrer le code que vous pouvez exécuter et tester avec quelques commentaires.

j'ai en fait fini avec deux implémentations. Je les appellerai implémentation A et implémentation B.

j'ai pensé qu'il serait intéressant de montrer les deux implémentations. J'espère que ça ne causera pas trop de confusion.

Pour l'enregistrement, la mise en Œuvre d'Un est la meilleure version.

Voici de brèves descriptions des deux implémentations:

Mise En Oeuvre A

cette version conserve la trace de l'état au niveau du composant App. La minuterie est gérée en passant props à la minuterie. La composante de minuterie ne conserve pas la trace de son propre temps laissé état cependant.

Mise En Oeuvre B

Cette version conserve la trace de l'état de minuterie au niveau du composant de minuterie à l'aide d'un TimerStore et d'un module TimerAction pour gérer l'état et les événements du composant.

l'inconvénient majeur (et probablement fatal) de l'implémentation B est que vous ne pouvez avoir qu'un seul composant de minuterie. Ceci est dû au fait que les modules TimerStore et TimerAction sont essentiellement des Singletons.


/

/

mise en oeuvre

/

/

cette version conserve la trace de l'état au niveau du composant App. La plupart des commentaires ici sont dans le code de cette version.

la minuterie est gérée en passant props à la minuterie.

changements de codes pour cette liste application:

  • app-constantes.js
  • app-actions.js
  • app-store.js
  • App.jsx
  • Timer.jsx

app-constantes.js

ici, je viens d'ajouter une constante pour rebrancher la minuterie.

module.exports = {
  START_TIMER: 'START_TIMER',
  STOP_TIMER: 'STOP_TIMER',
  RESET_TIMER: 'RESET_TIMER',
  CHANGE_PATTERN: 'CHANGE_PATTERN'
};

app-actions.js

je viens d'ajouter une méthode de régulation pour gérer le reset timer action.

var AppConstants = require('../constants/app-constants.js');
var AppDispatcher = require('../dispatchers/app-dispatcher.js');

var AppActions = {
  changePattern: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.CHANGE_PATTERN
    })
  },
  resetTimer: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.RESET_TIMER
    })
  },
  startTimer: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.START_TIMER
    })
  },
  stopTimer: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.STOP_TIMER
    })
  }
};

module.exports = AppActions;

app-store.js

voici où les choses changent un peu. J'ai ajouté des commentaires détaillés dans la ligne où j'ai fait des changements.

var AppDispatcher = require('../dispatchers/app-dispatcher.js');
var AppConstants = require('../constants/app-constants.js');
var EventEmitter = require('events').EventEmitter;
var merge = require('react/lib/Object.assign');


// I added a TimerStatus model (probably could go in its own file)
// to manage whether the timer is "start/stop/reset".
//
// The reason for this is that reset state was tricky to handle since the Timer
// component no longer has access to the "AppStore". I'll explain the reasoning for
// that later.
//
// To solve that problem, I added a `reset` method to ensure the state
// didn't continuously loop "reset". This is probably not very "Flux".
//
// Maybe a more "Flux" alternative is to use a separate TimerStore and
// TimerAction? 
//
// You definitely don't want to put them in AppStore and AppAction
// to make your timer component more reusable.
//
var TimerStatus = function(status) {
  this.status = status;
};

TimerStatus.prototype.isStart = function() {
  return this.status === 'start';
};

TimerStatus.prototype.isStop = function() {
  return this.status === 'stop';
};

TimerStatus.prototype.isReset = function() {
  return this.status === 'reset';
};

TimerStatus.prototype.reset = function() {
  if (this.isReset()) {
    this.status = 'start';
  }
};


var CHANGE_EVENT = "change";

var shapes = ['C', 'A', 'G', 'E', 'D'];
var rootNotes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'];

var boxShapes = require('../data/boxShapes.json');


// Added a variable to keep track of timer state. Note that this state is
// managed by the *App Component*.
var _timerStatus = new TimerStatus('start');


var _pattern = _setPattern();

function _setPattern() {
  var rootNote = _getRootNote();
  var shape = _getShape();
  var boxShape = _getBoxForShape(shape);

  _pattern = {
    rootNote: rootNote,
    shape: shape,
    boxShape: boxShape
  };

  return _pattern;
}

function _getRootNote() {
  return rootNotes[Math.floor(Math.random() * rootNotes.length)];
}

function _getShape() {
  return shapes[Math.floor(Math.random() * shapes.length)];
}

function _getBoxForShape(shape) {
  return boxShapes[shape];
}


// Simple function that creates a new instance of TimerStatus set to "reset"
function _resetTimer() {
  _timerStatus = new TimerStatus('reset');
}

// Simple function that creates a new instance of TimerStatus set to "stop"
function _stopTimer() {
  _timerStatus = new TimerStatus('stop');
}

// Simple function that creates a new instance of TimerStatus set to "start"
function _startTimer() {
  _timerStatus = new TimerStatus('start');
}


var AppStore = merge(EventEmitter.prototype, {
  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },


  // Added this function to get timer status from App Store
  getTimerStatus: function() {
    return _timerStatus;
  },


  getPattern: function() {
    return _pattern;
  },

  dispatcherIndex: AppDispatcher.register(function(payload) {
    var action = payload.action;

    switch(action.actionType) {
      case AppConstants.RESET_TIMER:
        // Handle reset action
        _resetTimer();
        break;
      case AppConstants.START_TIMER:
        // Handle start action
        _startTimer();
        break;
      case AppConstants.STOP_TIMER:
        // Handle stop action
        _stopTimer();
        break;
      case AppConstants.CHANGE_PATTERN:
        _setPattern();
        break;
    }

    AppStore.emitChange();

    return true;
  })
});

module.exports = AppStore;

App.jsx

il y a de nombreux changements dans App.jsx, en particulier nous avons déplacé l'État à la composante App de la composante timer. Des commentaires dans le code.

var React = require('react');

var Headline = require('./components/Headline.jsx');
var Scale = require('./components/Scale.jsx');
var RootNote = require('./components/RootNote.jsx');
var Shape = require('./components/Shape.jsx');
var Timer = require('./components/Timer.jsx');


// Removed AppActions and AppStore from Timer component and moved
// to App component. This is done to to make the Timer component more
// reusable.
var AppActions = require('./actions/app-actions.js');
var AppStore = require('./stores/app-store.js');


// Use the AppStore to get the timerStatus state
function getAppState() {
  return {
    timerStatus: AppStore.getTimerStatus()
  }
}

var App = React.createClass({
  getInitialState: function() {
    return getAppState();
  },


  // Listen for change events in AppStore
  componentDidMount: function() {
    AppStore.addChangeListener(this.handleChange);
  },


  // Stop listening for change events in AppStore
  componentWillUnmount: function() {
    AppStore.removeChangeListener(this.handleChange);
  },


  // Timer component has status, defaultTimeout attributes.
  // Timer component has an onTimeout event (used for changing pattern)
  // Add three basic buttons for Start/Stop/Reset
  render: function() {
    return (
      <div>
        <header>
          <Headline />
          <Scale />
        </header>
        <section>
          <RootNote />
          <Shape />
          <Timer status={this.state.timerStatus} defaultTimeout="15" onTimeout={this.handleTimeout} />
          <button onClick={this.handleClickStart}>Start</button>
          <button onClick={this.handleClickStop}>Stop</button>
          <button onClick={this.handleClickReset}>Reset</button>
        </section>
      </div>
    );
  },


  // Handle change event from AppStore
  handleChange: function() {
    this.setState(getAppState());
  },


  // Handle timeout event from Timer component
  // This is the signal to change the pattern.
  handleTimeout: function() {
    AppActions.changePattern();
  },


  // Dispatch respective start/stop/reset actions
  handleClickStart: function() {
    AppActions.startTimer();
  },
  handleClickStop: function() {
    AppActions.stopTimer();
  },
  handleClickReset: function() {
    AppActions.resetTimer();
  }
});

module.exports = App;

Timer.jsx

le Timer a beaucoup de changements depuis que j'ai enlevé le AppStore et AppActions dépendances pour faire le Timer composant plus réutilisable. Les commentaires détaillés se trouvent dans le code.

var React = require('react');


// Add a default timeout if defaultTimeout attribute is not specified.
var DEFAULT_TIMEOUT = 60;

var Timer = React.createClass({

  // Normally, shouldn't use props to set state, however it is OK when we
  // are not trying to synchronize state/props. Here we just want to provide an option to specify
  // a default timeout.
  //
  // See http://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html)
  getInitialState: function() {
    this.defaultTimeout = this.props.defaultTimeout || DEFAULT_TIMEOUT;
    return {
      timeLeft: this.defaultTimeout
    };
  },


  // Changed this to `clearTimeout` instead of `clearInterval` since I used `setTimeout`
  // in my implementation
  componentWillUnmount: function() {
    clearTimeout(this.interval);
  },

  // If component updates (should occur when setState triggered on Timer component
  // and when App component is updated/re-rendered)
  //
  // When the App component updates we handle two cases:
  // - Timer start status when Timer is stopped
  // - Timer reset status. In this case, we execute the reset method of the TimerStatus
  //   object to set the internal status to "start". This is to avoid an infinite loop
  //   on the reset case in componentDidUpdate. Kind of a hack...
  componentDidUpdate: function() {
    if (this.props.status.isStart() && this.interval === undefined) {
      this._tick();
    } else if (this.props.status.isReset()) {
      this.props.status.reset();
      this.setState({timeLeft: this.defaultTimeout});
    }
  },

  // On mount start ticking
  componentDidMount: function() {
    this._tick();
  },


  // Tick event uses setTimeout. I find it easier to manage than setInterval.
  // We just keep calling setTimeout over and over unless the timer status is
  // "stop".
  //
  // Note that the Timer states is handled here without a store. You could probably
  // say this against the rules of "Flux". But for this component, it just seems unnecessary
  // to create separate TimerStore and TimerAction modules.
  _tick: function() {
    var self = this;
    this.interval = setTimeout(function() {
      if (self.props.status.isStop()) {
        self.interval = undefined;
        return;
      }
      self.setState({timeLeft: self.state.timeLeft - 1});
      if (self.state.timeLeft <= 0) {
        self.setState({timeLeft: self.defaultTimeout});
        self.handleTimeout();
      }
      self._tick();
    }, 1000);
  },

  // If timeout event handler passed to Timer component,
  // then trigger callback.
  handleTimeout: function() {
    if (this.props.onTimeout) {
      this.props.onTimeout();
    }
  }
  render: function() {
    return (
      <small className="timer">
        ({ this.state.timeLeft })
      </small>
    )
  },
});

module.exports = Timer;

/

/

Mise En Oeuvre B

/

/

changements de Code inscription:

  • app-constantes.js
  • timer-actions.js (nouveau)
  • timer-store.js (nouveau)
  • app-store.js
  • App.jsx
  • Timer.jsx

app-constantes.js

Il devrait probablement aller dans un fichier nommé minuterie-constantes.js depuis qu'ils traitent avec le minuteur composant.

module.exports = {
  START_TIMER: 'START_TIMER',
  STOP_TIMER: 'STOP_TIMER',
  RESET_TIMER: 'RESET_TIMER',
  TIMEOUT: 'TIMEOUT',
  TICK: 'TICK'
};

timer-actions.js

ce module est explicite. J'ai ajouté trois événements-timeout, tick, et reset. Voir le code pour plus de détails.

var AppConstants = require('../constants/app-constants.js');
var AppDispatcher = require('../dispatchers/app-dispatcher.js');

module.exports = {

  // This event signals when the timer expires.
  // We can use this to change the pattern.
  timeout: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.TIMEOUT
    })
  },

  // This event decrements the time left
  tick: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.TICK
    })
  },

  // This event sets the timer state to "start"
  start: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.START_TIMER
    })
  },

  // This event sets the timer state to "stop"
  stop: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.STOP_TIMER
    })
  },

  // This event resets the time left and sets the state to "start"
  reset: function() {
    AppDispatcher.handleViewAction({
      actionType: AppConstants.RESET_TIMER
    })
  },
};

timer-store.js

j'ai séparé le minuteur du AppStore. C'est pour rendre le composant de minuterie un peu plus réutilisable.

La Minuterie magasin conserve la trace de la suite état:

  • timer statut - Peut-être "start" ou "stop"
  • gauche - le Temps restant sur la minuterie

La Minuterie de magasin gère les événements suivants:

  • l'événement de démarrage du minuteur définit l'état du minuteur pour démarrer.
  • l'événement timer stop définit l'état du timer pour l'arrêter.
  • L'événement tick décrémente le temps laissé par 1
  • l'événement de réinitialisation de la minuterie fixe l'heure restante par défaut et jeux de minuterie statut pour démarrer

Voici le code:

var AppDispatcher = require('../dispatchers/app-dispatcher.js');
var AppConstants = require('../constants/app-constants.js');
var EventEmitter = require('events').EventEmitter;
var merge = require('react/lib/Object.assign');

var CHANGE_EVENT = "change";
var TIMEOUT_SECONDS = 15;

var _timerStatus = 'start';
var _timeLeft = TIMEOUT_SECONDS;

function _resetTimer() {
  _timerStatus = 'start';
  _timeLeft = TIMEOUT_SECONDS;
}

function _stopTimer() {
  _timerStatus = 'stop';
}

function _startTimer() {
  _timerStatus = 'start';
}

function _decrementTimer() {
  _timeLeft -= 1;
}

var TimerStore = merge(EventEmitter.prototype, {
  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getTimeLeft: function() {
    return _timeLeft;
  },

  getStatus: function() {
    return _timerStatus;
  },

  dispatcherIndex: AppDispatcher.register(function(payload) {
    var action = payload.action;

    switch(action.actionType) {
      case AppConstants.START_TIMER:
        _startTimer();
        break;
      case AppConstants.STOP_TIMER:
        _stopTimer();
        break;
      case AppConstants.RESET_TIMER:
        _resetTimer();
        break;
      case AppConstants.TIMEOUT:
        _resetTimer();
        break;
      case AppConstants.TICK:
        _decrementTimer();
        break;
    }

    TimerStore.emitChange();

    return true;
  })
});

module.exports = TimerStore;

app-store.js

Cela pourrait être nommé pattern-store.js, bien que vous aurez besoin de faire quelques modifications pour qu'il soit réutilisable. Plus précisément, j'écoute directement la minuterie!--19--> action/événement pour déclencher un changement de motif. Vous ne voulez probablement pas cette dépendance si vous voulez réutiliser le changement de modèle. Par exemple, si vous vouliez changer le modèle par en cliquant sur un bouton ou quelque chose.

mis à part cela, je viens de supprimer toutes les fonctionnalités liées aux minuteries de AppStore.

var AppDispatcher = require('../dispatchers/app-dispatcher.js');
var AppConstants = require('../constants/app-constants.js');
var EventEmitter = require('events').EventEmitter;
var merge = require('react/lib/Object.assign');

var CHANGE_EVENT = "change";

var shapes = ['C', 'A', 'G', 'E', 'D'];
var rootNotes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'];

var boxShapes = require('../data/boxShapes.json');

var _pattern = _setPattern();

function _setPattern() {
  var rootNote = _getRootNote();
  var shape = _getShape();
  var boxShape = _getBoxForShape(shape);

  _pattern = {
    rootNote: rootNote,
    shape: shape,
    boxShape: boxShape
  };

  return _pattern;
}

function _getRootNote() {
  return rootNotes[Math.floor(Math.random() * rootNotes.length)];
}

function _getShape() {
  return shapes[Math.floor(Math.random() * shapes.length)];
}

function _getBoxForShape(shape) {
  return boxShapes[shape];
}

var AppStore = merge(EventEmitter.prototype, {
  emitChange: function() {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getPattern: function() {
    return _pattern;
  },

  dispatcherIndex: AppDispatcher.register(function(payload) {
    var action = payload.action;

    switch(action.actionType) {
      case AppConstants.TIMEOUT:
        _setPattern();
        break;
    }

    AppStore.emitChange();

    return true;
  })
});

module.exports = AppStore;

App.jsx

ici j'ai juste ajouté quelques boutons pour démarrer / arrêter / réinitialiser. Sur click, une TimerAction est expédiée. Donc, si vous avez cliqué sur le bouton "stop", nous appelons TimerAction.stop()

var React = require('react');

var Headline = require('./components/Headline.jsx');
var Scale = require('./components/Scale.jsx');
var RootNote = require('./components/RootNote.jsx');
var Shape = require('./components/Shape.jsx');
var Timer = require('./components/Timer.jsx');
var TimerActions = require('./actions/timer-actions.js');


var App = React.createClass({
  render: function() {
    return (
      <div>
        <header>
          <Headline />
          <Scale />
        </header>
        <section>
          <RootNote />
          <Shape />
          <Timer />
          <button onClick={this.handleClickStart}>Start</button>
          <button onClick={this.handleClickStop}>Stop</button>
          <button onClick={this.handleClickReset}>Reset</button>
        </section>
      </div>
    );
  },
  handleClickStart: function() {
    TimerActions.start();
  },
  handleClickStop: function() {
    TimerActions.stop();
  },
  handleClickReset: function() {
    TimerActions.reset();
  }
});

module.exports = App;

Timer.jsx

un des principaux changements est que nous utilisons une TimerAction et TimerStore au lieu de L'AppAction et AppStore qui a été utilisé à l'origine. La raison est d'essayer de rendre le composant Timer un peu plus réutilisable.

Le compte à rebours est l'état suivant:

  • statut Timer état peut être "start" ou "stop"
  • timeLeft le Temps restant sur la minuterie

Notez que j'ai utilisé setTimeout au lieu de setInterval. J'ai trouver setTimeout plus facile à gérer.

la majorité des la logique est dans le _tick méthode. En gros, nous avons continuer à l'appeler setTimeout tant que le statut est "en marche".

lorsque le minuteur atteint zéro, alors nous signalons le timeout événement. Le TimerStore et L'AppStore sont à l'écoute de cet événement.

  1. Le TimerStore aura simplement pour effet de réinitialiser le chronomètre. Même l'événement de réinitialisation.
  2. L'AppStore va changer le modèle.

si la minuterie n'a pas atteint zéro, nous soustrayons une seconde en signalant le "tick" de l'événement.

enfin, nous avons besoin de gérer le cas où le minuteur est arrêté et ensuite commencé. Cela peut être manipulé par l' componentDidUpdate crochet. Ce crochet est appelé lorsque l'état du composant change ou que le composant parent est rendu de nouveau.

Dans le componentDidUpdate méthode, nous nous assurons de lancer le "ticking" seulement si l'état est "start" et l'Identificateur de timeout est indéfini. Nous ne voulons pas que plusieurs setTimeouts s'exécutent.

var React = require('react');

var TimerActions = require('../actions/timer-actions.js');
var TimerStore = require('../stores/timer-store.js');

function getTimerState() {
  return {
    status: TimerStore.getStatus(),
    timeLeft: TimerStore.getTimeLeft()
  }
}

var Timer = React.createClass({
  _tick: function() {
    var self = this;
    this.interval = setTimeout(function() {
      if (self.state.status === 'stop') {
        self.interval = undefined;
        return;
      }

      if (self.state.timeLeft <= 0) {
        TimerActions.timeout();
      } else {
        TimerActions.tick();
      }
      self._tick();
    }, 1000);
  },
  getInitialState: function() {
    return getTimerState();
  },
  componentDidMount: function() {
    TimerStore.addChangeListener(this.handleChange);
    this._tick();
  },
  componentWillUnmount: function() {
    clearTimeout(this.interval);
    TimerStore.removeChangeListener(this.handleChange);
  },
  handleChange: function() {
    this.setState(getTimerState());
  },
  componentDidUpdate: function() {
    if (this.state.status === 'start' && this.interval === undefined) {
      this._tick();
    }
  },
  render: function() {
    return (
      <small className="timer">
        ({ this.state.timeLeft })
      </small>
    )
  }
});

module.exports = Timer;
19
répondu Gohn67 2014-12-27 13:37:41

Ne pas stocker l'état des composants

l'une des principales raisons d'utiliser flux est centraliser l'état de l'application. À cette fin, vous devez vous évitez d'utiliser un composant setState fonction. De plus, dans la mesure où les composants sauvegardent leur propre état, il ne devrait s'agir que de données d'état de nature très fugace (par exemple, vous pouvez définir l'état localement sur un composant qui indique si une souris est en vol stationnaire).

Utiliser Les Créateurs D'Action pour les opérations asynchrones

Dans Le Flux, magasins sont destinés à être synchrone. (Notez que c'est un point quelque peu controversé parmi les implémentations de Flux, mais je suggère certainement que vous faites des magasins synchrones. Une fois que vous autorisez l'opération async dans les magasins, cela brise le flux de données unidirectionnel et nuit au raisonnement de l'application.). Au lieu de cela, l'opération async devrait vivre dans votre Créateur. Dans votre code je ne vois aucune mention d'un créateur D'Action, donc je soupçonne ceci pourrait être la source de votre confusion. Néanmoins, votre Timer doit vivre dans le créateur D'Action. Si votre component doit activer le timer, il peut appeler une méthode sur L'Action Creator, L'Action Creator peut créer / gérer le timer, et le timer peut envoyer des événements qui seront gérés par le magasin.

mise à Jour: notez qu'à la 2014 react-conf Flux panel un développeur travaillant sur une application Flux large a déclaré que pour cette application particulière permet des opérations de récupération de données asynchrones dans les magasins (GETs mais pas PUTs ou POSTs).

Facebook's Flux Flow Chart

7
répondu Gil Birman 2015-03-20 18:34:44

Je retirerais la minuterie du magasin, et pour l'instant, je gérerais juste les patrons là. Votre minuterie aurait besoin de quelques petites modifications:

var Timer = React.createClass({
  _tick: function() {
    if (this.state.timeLeft < 0) {
      AppActions.changePattern();
      clearInterval(this.interval);
    } else {
      this.setState({ timeLeft: this.state.timeLeft - 1 });
    }
  },
  _onChange: function() {
    // do what you want with the pattern here
    // or listen to the AppStore in another component
    // if you need this somewhere else
    var pattern = AppStore.getPattern();
  },
  getInitialState: function() {
    return { timeLeft: 60 };
  },
  componentWillUnmount: function() {
    clearInterval(this.interval);
  },
  componentDidMount: function() {
    this.interval = setInterval(this._tick, 1000);
    AppStore.addChangeListener(this._onChange);
  },
  render: function() {
    return (
      <small>
        ({ this.state.timeLeft })
      </small>
    )
  }
});
2
répondu Shawn 2014-12-22 20:00:59