Please specify:
(Not filling out _all fields_ of this template will probably result in your issue being closed without further notice. If you are not willing fill it in, why would someone else spent his time fixing it?)
https://github.com/billdwhite/ngrx-immer/tree/ngrx-immer
Check out, run npm install, then run using
'ng serve'
it loads fine; now use AOT compilation:
'ng serve --aot'
or use the angular.json run config:
'ng run ngrx-immer:serve:production'
no states are loaded
https://github.com/timdeschryver/ngrx-immer/issues/1
Turns out that you have to set up the reducers to return a function, not a const.
I add some code in case somebody else have same issue and come to this thread
Old code
export const reducer = produce<AppraisalState, AppraisalActions>((draftState, appraisalAction) => {
switch (appraisalAction.type) {
case AppraisalActionTypes.LoadAppraisals:
{
draftState.xx = appraisalAction.payload;
return;
...
}
}
}, initialState);
}
New Code
export function reducer(state: AppraisalState = initialState, action: Action) {
return produce<AppraisalState, AppraisalActions>((draftState, appraisalAction) => {
switch (appraisalAction.type) {
case AppraisalActionTypes.LoadAppraisals:
{
draftState.xx = appraisalAction.payload;
return;
...
}
}
})(state, action as AppraisalActions);
}
@RezaRahmati You can simplify that further:
export function reducer(state: AppraisalState = initialState, action: AppraisalActions) {
return produce(state, draftState => {
switch (appraisalAction.type) {
case AppraisalActionTypes.LoadAppraisals:
draftState.xx = appraisalAction.payload;
return;
}
});
}
With ngrx/store 8+ they have a new way to create reducers with createReducer and on.
I've made a small helper function for using immer with createReducer.
import produce, { Draft } from 'immer';
import { ActionCreator, createReducer, on } from '@ngrx/store';
import { ActionType, FunctionWithParametersType } from '@ngrx/store/src/models';
function produceOn<Type extends string, C extends FunctionWithParametersType<any, object>, State>(
actionType: ActionCreator<Type, C>,
callback: (draft: Draft<State>, action: ActionType<ActionCreator<Type, C>>) => any,
) {
return on(actionType, (state: State, action): State => produce(state, (draft) => callback(draft, action)));
}
// Usage:
const featureReducer = createReducer(
initialState,
produceOn(action, (draft, action) => {
// TODO STUFF
}
);
export function reducer(state = initialState, action) {
return featureReducer(state, action);
}
to add onto the example:
import {createReducer} from '@ngrx/store';
import {on} from "@ngrx/store";
import produce, {Draft} from "immer";
export const initialUserState: IUserState = {
knownUsers: [user1, user2],
selectedUser: null,
scenes: null
};
export function produceOn<Type extends string, C extends FunctionWithParametersType<any, object>, State>(
actionType: ActionCreator<Type, C>,
callback: (draft: Draft<State>, action: ActionType<ActionCreator<Type, C>>) => any,
) {return on(actionType, (state: State, action): State => produce(state, (draft) => callback(draft, action)));}
export const loadRequest = createAction('[Scenes API] Scene Load Request', props<{ businessId: BusinessId }>());
export const loadSuccess = createAction('[Scenes API] Scene Load Success', props<{ scenes: List<SceneModel> }>());
// ngrx 8+ with immer and support for on() within reducer
const featureReducer = createReducer(
initialUserState,
produceOn(loadSuccess, (draft, action) => {
draft.scenes = {myList: [1,2,3]};
}),
produceOn(loadFailure, (draft, action) => {
draft.scenes = {myList: []};
console.log('error loading...');
})
);
Thank you for solution! However, I see two potential problems:
@TekSiDoT if you want to use Immer with NgRx 8 in a type-safe way there's mutableOn from ngrx-etc.
const entityReducer = createReducer(
{
entities: {},
},
mutableOn(create, (state, { type, ...entity }) => {
state.entities[entity.id] = entity
}),
mutableOn(update, (state, { id, newName }) => {
const entity = state.entities[id]
if (entity) {
entity.name = newName
}
}),
mutableOn(remove, (state, { id }) => {
delete state.entities[id]
}),
)
Hi @TekSiDoT and @timdeschryver
question, What do you say produceOn accesses privates and is not type safe?
I wonder why as I do have type help and having a hard time understanding which private is being accessed. Thanks again for the link,
Sean.
@born2net : Sorry for the confusion, my bad, no privates accessed
@timdeschryver : thanks for the hint, we've since introduced mutableOn in our code base and are very happy with the results.
@born2net sorry, maybe type safe is badly worded.
What I meant is that the on function supports more than one action type is parameter, whereas produceOn does not - yes you could add more typings to support it.
In fact, what ngrx-etc does, is more or less the same code as in your snippet.
Most helpful comment
With ngrx/store 8+ they have a new way to create reducers with
createReducerandon.I've made a small helper function for using
immerwithcreateReducer.