-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is it possible to dispatch action from the store? #184
Comments
I think this would be the solution yes. componentDidMount () {
this.interval = setInterval(() => {
this.props.actions.doSomething()
}, 2000)
}
componentWillUnmount () {
clearInterval(this.interval)
} |
Yes, but it seems like a hack, not real "architectural" solution, because starting this timer is normal business logic and I cannot handle it in the store, but component. So, I have buttons to start and stop timer, but what if those buttons are not in the same component? What is right approach to handle this with redux stores and actions? |
I believe this is what async action creators for. You can not only dispatch actions from them asynchronously, but also dispatch multiple actions or none. The code may look something like this: import { TIMER_STARTED, TIMER_TICK, TIMER_STOPPED } from '../constants/ActionTypes';
export function startTimer() {
return (dispatch, getState) => {
const { timerId } = getState();
if (timerId === null) {
const timerId = setInterval(() => {
dispatch({type: TIMER_TICK})
}, 1000)
dispatch({type: TIMER_STARTED, payload: timerId}) // a store supposed to save `timerId`
}
}
}
export function stopTimer() {
return (dispatch, getState) => {
const { timerId } = getState();
if (timerId !== null) {
clearInterval(timerId)
dispatch({type: TIMER_STOPPED, payload: timerId}) // now store supposed to set `timerId` to `null`
}
}
} |
If this is business logic you do not want to handle in your component, you can create a business logic object, which does that for you. So instead of having this inside the component you would have an object which subscribes to your redux instance. If you want to start the timer you send out a const business = {
timer: null,
maybeStartTimer() {
// clean out old timer
clearInterval(business.timer);
// redux has been defined somewhere in scope
const { timerStore } = redux.getState();
if (timeStore.timerShouldStart) {
business.interval = setInterval(() => {
// the actions are accessible as well
redux.dispatch(doSomething());
}, 2000)
}
}
}
redux.subscribe(::business.maybeStartTimer); Stopping could then be done with a |
@rpominov I'm not sure I would store the timer id in the store, as it is not serializable (or rather, has no meaning if sent over the wire). But I do like your way of having the |
@johanneslumpe Good point, my solution probably won't work in an isomorphic app. |
@rpominov Great, I like your solution, and in isomorphic app I would handle timer initialization manually. |
I'd like to see this in #140 |
Would this be the best way to handle WebSockets as well or is there another pattern? |
@taylorhakes Seems sensible to me. |
So what is the solution to make a timer work? @rpominov's suggestion? |
I actually threw together a proof-of-concept for a timer middleware a while back. Haven't actually tested it myself, but ought to demonstrate the idea: https://gist.github.com/markerikson/ca96a82d6fdb29388aca4052a9455431 |
@rpominov What if I want to stop timer automatically, lets say after 10 ticks? The ticks accumulating logic will definitely be placed in TIMER_TICK reducer. But what about the logic to stop timer once 10 is reached? Should I call |
@ravensteel : I actually wrote a small sample middleware to allow controlling timers using actions. The code for that is at https://gist.github.com/markerikson/ca96a82d6fdb29388aca4052a9455431 . I haven't actually tested it, but something like that ought to work, I think. As for stopping the timer, the logic for checking that should _definitely not_ go in the reducer. You'd probably want to do that checking somewhere that has access to checking the state, such as a connected component, a middleware, or a saga. Basically, pretty much anything but a reducer. |
@markerikson But that generally means scattering the business logic down to visual components? Increasing the ticks counter inside a reducer is one part of it, but no way to react on the resulting value? |
Reducers are in charge of determining how the state updates based on the previous state and the incoming action. Other parts of the codebase should be looking at the updated state and determine what to do based on the new values, whether it be re-rendering with the latest data, or triggering some additional behavior based on the state change. Also, note that it's entirely possible to have React components that don't actually render anything at all, but simply use the lifecycle methods to wrap up additional behavior (looking at changes in props to see if something should happen, etc). |
@aputivlskiy @markerikson I don't like having business logic not related to component rendering in the components, so I usually have use Models that contain the higher-level business logic that affects the data in Stores, but is not related to rendering/tied to any one component. The Models have access to dispatch from the stores as well as the action creators etc. Lately, I've been use a library I wrote (https://www.npmjs.com/package/redux-actionize) as a wrapper for the action/reducer creation, and the get_actions method accepts the dispatch and does the binding automatically for all registered actions similar to how bindActionCreators works. So, Application.login_in_progress() is actually (1) creating the action of type "application_login_in_progress" and (2) dispatching the action. I find this higher-level abstraction into Models for some core business logic useful, especially when interacting with data from AJAX requests (e.g. an API) or running background tasks/timers (e.g. setInterval). Here's an example of an ApplicationModel that interacts with an API, and has a method for starting a timer:
Then, you can just use this model in your components to start the timer:
|
I know that this is bad idea, but I'm ultimately careful with this and don't know another option.
I have action handler inside the store which should emit actions through setInterval every few seconds. Is it possible or the only options is to move setInterval to component?
The text was updated successfully, but these errors were encountered: