-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Dispatch multiple actions at once #468
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
Comments
I'm not sure how it suppose to work, because reducers need to know previous state, therefore you would need to stop emitting updates somewhere after this point. store.dispatch( new AddItems([item1, item2]) ); |
It's really easy to implement just need to use state = actions.reduce( (nextState, action) => reducer(nextState, action), state); I thought that maybe my problem was about my reducer design but it seems not. small example : interface AuthorAndBooks {
author: Author;
books: Book[];
}
declare function getAuthors(): Observable<AuthorAndBooks[]>;
//effects
return getAuthors()
.mergeMap( authorsNBooks => Observable.of(
new AddAuthors(authorsNBooks.map( ({author}) => author )),
...authorsNBooks.map( ({author, books}) => new AddBooks(books, author.id) )
); |
I'm dealing with this by chaining reducers internally. I have small helper class: export function alterState<T extends Object>(source: T, change: Partial<T>): T {
return Object.assign({}, source, change);
}
export class Chain<T> {
static from<Ts>(value: Ts) {
return new Chain(value);
}
protected constructor(private value: T) {
}
map<R>(fn: (value: T) => R): Chain<R> {
return new Chain(fn(this.value));
}
end(): T {
return this.value;
}
} and then inside reducer I can run multiple steps to transform state internally return Chain.from(
alterState(state, {
products: List.of(...payload)
}))
.map(updateSummary)
.end(); |
Dispatching multiple actions is straightforward enough without changing the existing APIs to handle different code paths. [Action1, Action2].forEach(store.dispatch); |
@brandonroberts this is no different than my first snippet it'll emit a new state per action dispatched. So there will be a real impact in doing |
@ghetolay what you are proposing introduces a new behavior though. Batching multiple actions is better handled in your reducer. This project has an example of batching actions. https://gitlab.com/linagora/petals-cockpit/blob/master/frontend/src/app/shared/helpers/batch-actions.helper.ts#L34-42 |
Yeah I also had in mind creating an higher order action but it felt like doing
Doing it on the reducer means doing more complicated reducers. And doing stuff on reducers to reduce state refresh rate feels misplaced. Your link look like a workaround precisely because it's not handled by the Store natively. |
The 'batchActions' solution out of the box would be nice. |
Any possibility we could use the Redux package Redux-Batch-Actions in Ngrx/Store? |
If we break down redux-batched-actions to the basics it just a metareducer and a BatchAction. Thanks to @brandonroberts link to the batch-action helpers I created this little module while I am just sitting on my sofa and playing with my tablet around. So I can not test if this will work. I will try it in the next days if I am back on my development environment. Perhaps someone can try it earlier. @spock123?? : import { ActionReducer, Action } from "@ngrx/store";
/*
* File: batch-action-reducer.ts
*
* BatchAction - Use it to dispatch multiple Actions as one
*
* 0. Put this script somewhere in you app
*
* 1. Register enableBatching function as metareducer. Here an example
* with storeFreeze included
*
* import {enableBatchReducer} from './somewhere/batch-action-reducer'
*
* export const metaReducers: MetaReducer<AppState>[] = !environment.production ?
* [storeFreeze, enableBatchReducer] :
* [enableBatchReducer];
*
* @NgModule({
* ...
* imports: [
* ....
* StoreModule.forRoot(appReducer, { metaReducers }),
* ]
* ...
* })
*
* 2. Create an anonymous batch action. It has no own action type,
* so we use "Anonymous Batch Action" as default
*
* this.store.dispatch(new BatchAction([new FooAction(), new BarAction({bar: 'somePayload'})]))
*
* 3. You can make your own BatchAction if you want to dispatch another type,
* to be honest a "Anonymous Batch Action" type is very ugly in redux dev tools.
* Perhaps we would like to react on it in our Effects or Reducers, too. So we need a unqiue
* type.
*
* import { BatchAction } from './somewhere//batch-action-reducer
*
* export const MY_ACTION = 'My Action';
*
* export class MyAction extends BatchAction {
* readonly type = MY_ACTION;
* constructor(payload: any[]) { super(payload) }
* }
*
* this.store.dispatch(new MyAction([new FooAction(), new BarAction('somePayload')]));
*/
export class BatchAction implements Action {
public type = 'Anonymous Batch Action';
constructor(public payload: any[]) { }
}
export function enableBatchReducer<S>(reduce: ActionReducer<S>): ActionReducer<S> {
return function batchReducer(state: S, action: Action): S {
if (action instanceof BatchAction) {
let batchActions = action.payload;
return batchActions.reduce(batchReducer, state);
} else {
return reduce(state, action);
}
};
} |
Perhaps a better methode would be with an decorator, but I do not know how to do it. @BatchAction
class MyAction implements Action {
readonly type = MY_ACTION;
constructor(payload: Array<any>) {]
} |
After some research I have created my first decorator and my first own project on github for my own suggestion above. As far as I can say it works as aspected. Perhaps someone could play around with it and review my code. Would be great if we could have something like this out of the box. "Batteries included", you know.... |
The question that brought me here is rather an action split effect would produce two states. Could the batching be usefull there as well? |
@brandonroberts that solution won't work after rxjs6 update, as |
@rossanmol . Update 'MergeMapOperator' extend parameters
I'm using this version :
I will reported this issue on my project, and resolve that later may be but I don't have error in my console, just a little part of deprecated code ... Here the file of bacth actions helper : https://gitlab.com/linagora/petals-cockpit/blob/master/frontend/src/app/shared/helpers/batch-actions.helper.ts#L34-42 I stay tunned at all discussions about this. |
FYI syntax suggested by @brandonroberts
Here is a helper function that I added to my project to dispatch multiple actions without copying and pasting the above snippet:
|
I need something similar to what ngrx-batch-action-reducer offers, but a solution that also triggers effects for the individual actions. It's definitely desirable to be able to batch together a bunch of actions from the point of view of reducers, such that selectors are not triggered after each action; but in such a way that the effects are for each action are still honoured. |
@Hainesy Something like this should work. this.actions$.pipe(
ofType('BatchAction'),
concatMap(action => from(action.payload)),
ofType('EffectAction'),
...
) |
I'm submitting a...
Add the ability to dispatch multiple actions without emitting a new state for each action but only once all action are processed.
So instead of
we would get
I've already looked how it could be done, should be easy and I could do it if this is accepted.
The text was updated successfully, but these errors were encountered: