Handling a timer in React/Flux
Solution 1
I ended up downloading your code and implementing the start/stop/reset feature you wanted. I think that's probably the best way to explain things - to show code that you can run and test along with some comments.
I actually ended up with two implementations. I'll call them Implementation A and Implementation B.
I thought it would be interesting to show both implementations. Hopefully it doesn't cause too much confusion.
For the record, Implementation A is the better version.
Here are brief descriptions of both implementations:
Implementation A
This version keeps track of the state at the App component level. The timer is managed by passing props
to the Timer component. The timer component does keep track of it's own time left state though.
Implementation B
This version keeps track of the timer state at the the Timer component level using a TimerStore and TimerAction module to manage state and events of the component.
The big (and probably fatal) drawback of implementation B is that you can only have one Timer component. This is due to the TimerStore and TimerAction modules essentially being Singletons.
|
|
Implementation A
|
|
This version keeps track of the state at the App component level. Most of the comments here are in the code for this version.
The timer is managed by passing props
to the Timer.
Code changes listing for this implementation:
- app-constants.js
- app-actions.js
- app-store.js
- App.jsx
- Timer.jsx
app-constants.js
Here I just added a constant for reseting the timer.
module.exports = {
START_TIMER: 'START_TIMER',
STOP_TIMER: 'STOP_TIMER',
RESET_TIMER: 'RESET_TIMER',
CHANGE_PATTERN: 'CHANGE_PATTERN'
};
app-actions.js
I just added a dispatch method for handling the 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
Here is where things change a bit. I added detailed comments inline where I made changes.
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
There are numerous changes in App.jsx, specifically we have moved the state to the App component from the timer component. Again detailed comments in the 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
The Timer
has many changes as well since I removed the AppStore
and AppActions
dependencies to make the Timer
component more reusable. Detailed comments are in the 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;
|
|
Implementation B
|
|
Code changes listing:
- app-constants.js
- timer-actions.js (new)
- timer-store.js (new)
- app-store.js
- App.jsx
- Timer.jsx
app-constants.js
These should probably go in a file named timer-constants.js since they deal with the Timer component.
module.exports = {
START_TIMER: 'START_TIMER',
STOP_TIMER: 'STOP_TIMER',
RESET_TIMER: 'RESET_TIMER',
TIMEOUT: 'TIMEOUT',
TICK: 'TICK'
};
timer-actions.js
This module is self-explanatory. I added three events - timeout, tick, and reset. See code for details.
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
I separated out the timer stuff from the AppStore
. This is to make the Timer component a bit more reusable.
The Timer store keeps track of the following state:
- timer status - Can be "start" or "stop"
- time left - Time left on timer
The Timer store handles the following events:
- The timer start event sets timer status to start.
- The timer stop event sets timer status to stop.
- The tick event decrements the time left by 1
- The timer reset event sets the time left to the default and sets timer status to start
Here is the 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
This could be named pattern-store.js
, although you'd need to make some changes for it to be reusable. Specifically, I'm directly listening for the Timer's TIMEOUT
action/event to trigger a pattern change. You likely don't want that dependency if you want to reuse pattern change. For example if you wanted to change the pattern by clicking a button or something.
Aside from that, I just removed all the Timer related functionality from the 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
Here I just added some buttons for start/stop/reset. On click, a TimerAction is dispatched. So if you clicked the "stop" button, we call 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
One of the main changes is that we are using a TimerAction and TimerStore instead of the AppAction and AppStore that was used originally. The reason is to try to make the Timer component a bit more reusable.
The Timer has the following state:
- status Timer status can be "start" or "stop"
- timeLeft Time left on timer
Note that I used setTimeout
instead of setInterval
. I find setTimeout
easier to manage.
The bulk of the logic is in the _tick
method. Basically we keep calling setTimeout
so long as the status is "start".
When the timer reaches zero, then we signal the timeout
event. The TimerStore and AppStore are listening for this event.
- The TimerStore will merely reset the timer. Same the reset event.
- The AppStore will change the pattern.
If the timer not reached zero, we subtract one second by signaling the "tick" event.
Lastly we need to handle the case where the timer is stopped and then later started. This can be handled through the componentDidUpdate
hook. This hook gets called when the component's state changes or the parent components gets re-rendered.
In the componentDidUpdate
method, we make sure to start the "ticking" only if the status is "start" and the timeout identifier is undefined. We don't want multiple setTimeouts running.
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;
Solution 2
Don't store state in components
One of the main reasons to use flux is to centralize application state. To that end, you should avoid using a component's setState
function at all. Furthermore, to the extent that components save their own state, it should only be for state data of a very fleeting nature (For example, you might set state locally on a component that indicates if a mouse is hovering).
Use Action Creators for async operations
In Flux, stores are meant to be synchronous. (Note that this is a somewhat contentious point among Flux implementations, but I definitely suggest that you make stores synchronous. Once you allow async operation in Stores, it breaks the unidirectional data flow and impairs application reasoning.). Instead, async operation should live in your Action Creator. In your code I see no mention of an Action Creator, so I suspect this might be the source of your confusion. Nevertheless, your actual Timer should live in the Action Creator. If your component needs to effect the timer, it can call a method on the Action Creator, the Action Creator can create/manage the timer, and the timer can dispatch events which will be handled by the store.
Update: Note that at the 2014 react-conf Flux panel one developer working on a large Flux application said that for that particular application they do allow async data fetching operations in the stores (GETs but not PUTs or POSTs).
Solution 3
I would remove the timer from the store, and for now, just manage the patterns there. Your timer component would need a couple small changes:
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>
)
}
});
Joris Ooms
Updated on June 09, 2022Comments
-
Joris Ooms about 2 years
I'm working on an application where I want a timer to countdown from, say, 60 seconds to 0 and then change some content, after which the timer restarts again at 60.
I have implemented this in React and Flux but since I'm new to this, I'm still running into some problems.
I now want to add a start/stop button for the timer. I'm not sure where to put/handle the timer state.
I have a component
Timer.jsx
which looks like this: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;
It retrieves a countdown duration from the store, where I simply have:
var _timeLeft = 60;
Now, when I want to implement a start/stop button, I feel like I should also implement this through Flux Actions, correct? So I was thinking of having something like this in my store:
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; })
However, since my Timer component currently handles the setInterval, I don't know how to get my START/STOP_TIMER events working. Should I move the setInterval stuff from the Timer component to the Store and somehow pass this down to my component?
Full code can be found here.
-
Joris Ooms over 9 yearsHey, thanks for the reply. I have been trying this, but I still can't figure out how to properly implement it. I understand I can move the timeLeft stuff to the Timer component, but now I'm looking at buttons to start/pause/stop and they are in their own components (<Control />). Perhaps putting everything in the store would be better, and then trigger actions/events where necessary?
-
Shawn over 9 yearsAre those buttons children of this component? If so you pass in callbacks, so you can control the interval timer from this Timer component: <StopButton onClick={ this.stopTimer } />. You may need to handle the click inside the button implementation, something like this.props.onClick(e).
-
Joris Ooms over 9 yearsThey aren't children of the Timer component, no. I'm close to giving up because I have no idea how to do this. Might even put a bounty on this question to see if someone can explain this to me. I really liked React/Flux to build a quick prototype, but now that I want to do more "complex" (simple timer, heh..) stuff, I'm running into a wall. Might have to read more about it.
-
Shawn over 9 yearsWell, like I said in my original comment, if multiple comments are working with the time interval, you should probably manage it all externally in a timer store. That store would dispatch a change event every tick and the subscribed components would do what they need with change handlers.
-
Joris Ooms over 9 yearsThanks for your answer. I hadn't heard of 'action creators' before. Is this an example of them? github.com/facebook/flux/blob/master/examples/flux-todomvc/js/… It seems I have a lot of reading to do :) I will keep your answer in the back of my mind.
-
Joris Ooms over 9 yearsThank you so much for your time and effort. I will go over both implementations and try to understand what I was doing wrong, or where my thought process went wrong at least. Does your implementation follow the rules Gil Berman mentioned in his answer? I hadn't heard of action creators, nor did I know about not using setState. I still have a lot to learn/read about Flux it seems. Thank you so much!
-
Gohn67 over 9 years@cabaret I have not heard of Action Creators until Gil mentioned them. I need to look into that too because I know what he means about the async operations messing up data flow. My second implementation is closer to the rules that Gil mentioned (Don't store state in components).
-
Joris Ooms over 9 yearsHa, guess we'll both have to look into that then. I'll try and implement both your solutions, see how they differ and most importantly try to understand where I went wrong. I have a feeling I don't know some crucial 'rules' of Flux; then again, the documentation on it seems to be so sparse.
-
Gohn67 over 9 years@cabaret I made an update to my answer. I switched the order of implementations. The "Alternate implementation" is now "Implementation A". This is my preferred solution. "Implementation B" has a big drawback in that the TimerStore and TimerAction modules are essentially singletons. This means you couldn't use multiple timer components at the same time.
-
Joris Ooms over 9 yearsOkay, sounds good. I was going over (what now is) implementation A ('alternate') and saw the comments about things not being very 'Flux', so I'm trying to figure out how to 'flux' it ;) Thanks again for your time!
-
Gil Birman over 9 yearsyes, and here's another example