Skip to content

Instantly share code, notes, and snippets.

@born2net
Created July 10, 2019 14:55
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save born2net/0a8e47a80e1e09fe984f57763680dc86 to your computer and use it in GitHub Desktop.
Save born2net/0a8e47a80e1e09fe984f57763680dc86 to your computer and use it in GitHub Desktop.
ngrx 8+ with immer and support for on() within reducer
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...');
})
);
@luchillo17
Copy link

@born2net yours wasn't putting the draft type in the callback, after fiddling I came up with this one, I think it's simpler:

export function produceOn<C1 extends ActionCreator, S>(
  actionType: C1,
  callback: (draft: Draft<S>, action: ActionType<C1>) => any,
) {
  return on(
    actionType,
    (state: S, action: ActionType<C1>): S =>
      produce(state, (draft) => callback(draft, action)),
  );
}

@xiodine32
Copy link

@luchillo17 I've expanded it to help autocomplete

export function produceOn<C1 extends ActionCreator, S>(
  actionType: C1,
  callback: (draft: Draft<S>, action: ActionType<C1>) => any,
): On<S> {
  return on(
    actionType,
    (state: S, action: ActionType<C1>): S =>
      produce(state, (draft: Draft<S>) => callback(draft, action)),
  );
}

@DavidTheProgrammer
Copy link

DavidTheProgrammer commented Feb 10, 2021

@luchillo17 I've expanded it to help autocomplete

export function produceOn<C1 extends ActionCreator, S>(
  actionType: C1,
  callback: (draft: Draft<S>, action: ActionType<C1>) => any,
): On<S> {
  return on(
    actionType,
    (state: S, action: ActionType<C1>): S =>
      produce(state, (draft: Draft<S>) => callback(draft, action)),
  );
}

The "On<S>" leads to a compilation error. Is there an import I'm missing or something

@luchillo17
Copy link

@DavidTheProgrammer I didn't check on @xiodine32 answer but I don't see where the On<S> interface is defined, maybe it comes from @ngrx/store, try importing it.

@DavidTheProgrammer
Copy link

@luchillo17 I think maybe it was a typo or something; I can't find any definitions of it anywhere.

@xiodine32
Copy link

xiodine32 commented Apr 28, 2021

Sorry @DavidTheProgrammer, @luchillo17 On<S> was an internal type I declared to help me with the return type.

Below is my code for ngrx ^11.0.1 and immer ^8.0.1.
I wanted the reducer's state to autocomplete as if immer was not included in the project, I view the code as "dirty", open to suggestions!

export function produceOn<State, Creators extends ActionCreator>(
  creators: Creators,
  reducer: (state: State, action: ActionType<Creators>) => void,
): ReducerTypes<State, Creators[]> {
  return on(
    creators,
    (state, action) => produce(state, draft => reducer(draft as State, action)) as State,
  ) as unknown as ReducerTypes<State, Creators[]>;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment