Skip to content

Instantly share code, notes, and snippets.

@krzysztof-miemiec
Last active September 3, 2019 22:18
Show Gist options
  • Save krzysztof-miemiec/4b0d443fdca966bcd4a092409e9dd6cf to your computer and use it in GitHub Desktop.
Save krzysztof-miemiec/4b0d443fdca966bcd4a092409e9dd6cf to your computer and use it in GitHub Desktop.
TypeScript Redux Actions
type FunctionType = (...args: any[]) => any;
type ActionCreatorsMapObject = { [actionCreator: string]: FunctionType & { type?: string } };
export type ActionsUnion<A extends ActionCreatorsMapObject> = ReturnType<A[keyof A]>;
export type ActionCreator<T, Payload> = { type: T } & ((...args: any[]) => { type: T } & Payload);
// No args
export function createAction<T extends string, Payload extends {}>(
type: T, creator?: () => Payload,
): { type: T } & (() => { type: T } & Payload);
// 1 arg, 1 maybe
export function createAction<T extends string, Payload extends {}, Arg1>(
type: T, creator: (arg1?: Arg1) => Payload,
): { type: T } & ((arg1?: Arg1) => { type: T } & Payload);
// 1 arg
export function createAction<T extends string, Payload extends {}, Arg1>(
type: T, creator: (arg1: Arg1) => Payload,
): { type: T } & ((arg1: Arg1) => { type: T } & Payload);
// 2 args, 2 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2>(
type: T, creator: (arg1?: Arg1, arg2?: Arg2) => Payload,
): { type: T } & ((arg1?: Arg1, arg2?: Arg2) => { type: T } & Payload);
// 2 args, 1 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2>(
type: T, creator: (arg1: Arg1, arg2?: Arg2) => Payload,
): { type: T } & ((arg1: Arg1, arg2?: Arg2) => { type: T } & Payload);
// 2 args
export function createAction<T extends string, Payload extends {}, Arg1, Arg2>(
type: T, creator: (arg1: Arg1, arg2: Arg2) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2) => { type: T } & Payload);
// 3 args, 3 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3>(
type: T, creator: (arg1?: Arg1, arg2?: Arg2, arg3?: Arg3) => Payload,
): { type: T } & ((arg1?: Arg1, arg2?: Arg2, arg3?: Arg3) => { type: T } & Payload);
// 3 args, 2 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3>(
type: T, creator: (arg1: Arg1, arg2?: Arg2, arg3?: Arg3) => Payload,
): { type: T } & ((arg1: Arg1, arg2?: Arg2, arg3?: Arg3) => { type: T } & Payload);
// 3 args, 1 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3>(
type: T, creator: (arg1: Arg1, arg2: Arg2, arg3?: Arg3) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2, arg3?: Arg3) => { type: T } & Payload);
// 3 args
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3>(
type: T, creator: (arg1: Arg1, arg2: Arg2, arg3: Arg3) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2, arg3: Arg3) => { type: T } & Payload);
// 4 args, 4 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3, Arg4>(
type: T, creator: (arg1?: Arg1, arg2?: Arg2, arg3?: Arg3, arg4?: Arg4) => Payload,
): { type: T } & ((arg1?: Arg1, arg2?: Arg2, arg3?: Arg3, arg4?: Arg4) => { type: T } & Payload);
// 4 args, 3 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3, Arg4>(
type: T, creator: (arg1: Arg1, arg2?: Arg2, arg3?: Arg3, arg4?: Arg4) => Payload,
): { type: T } & ((arg1: Arg1, arg2?: Arg2, arg3?: Arg3, arg4?: Arg4) => { type: T } & Payload);
// 4 args, 2 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3, Arg4>(
type: T, creator: (arg1: Arg1, arg2: Arg2, arg3?: Arg3, arg4?: Arg4) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2, arg3?: Arg3, arg4?: Arg4) => { type: T } & Payload);
// 4 args, 1 maybe
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3, Arg4>(
type: T, creator: (arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4?: Arg4) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4?: Arg4) => { type: T } & Payload);
// 4 args
export function createAction<T extends string, Payload extends {}, Arg1, Arg2, Arg3, Arg4>(
type: T, creator: (arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4) => Payload,
): { type: T } & ((arg1: Arg1, arg2: Arg2, arg3: Arg3, arg4: Arg4) => { type: T } & Payload);
// Any args & basic definition
export function createAction<T extends string, Payload extends {}>(
type: T, actionPayloadCreator?: (...args: any[]) => Payload,
) {
const creator = (...args: any[]) => ({
type,
...(actionPayloadCreator ? actionPayloadCreator(...args) as any : {}),
});
creator.type = type;
return creator as ActionCreator<T, Payload>;
}
import { Omit } from 'react-redux';
import { Action, AnyAction, Reducer } from 'redux';
import { PersistConfig, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import storage from 'redux-persist/lib/storage';
import { createReducer } from './createReducer.util';
const defaultPersistConfig = {
storage,
stateReconciler: autoMergeLevel2,
timeout: 2000,
};
type PartialPersistConfig = Omit<PersistConfig, 'storage'>;
export const createPersistedReducer = <S extends object, A extends Action = AnyAction>
(reducer: Reducer<S, A>) =>
(persistConfig: PartialPersistConfig): Reducer<S, A> =>
persistReducer(
{ ...defaultPersistConfig, ...persistConfig },
createReducer(reducer),
) as Reducer;
import { Action, AnyAction, Reducer } from 'redux';
export const createReducer = <S extends {}, A extends Action = AnyAction>
(reducer: Reducer<S, A>) =>
(state: S | undefined, action: A): S =>
action.type === AUTH_ACTIONS.CLEAR_STORAGE
? reducer(undefined, { ...(action as object), snapshot: state } as any)
: reducer(state, action);
import { Action } from 'redux';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { ActionCreator } from './createAction.util';
// 3 action types
export function ofType<T extends string, Payload extends {},
T2 extends string, Payload2 extends {},
T3 extends string, Payload3 extends {}>(
creator1: ActionCreator<T, Payload>,
creator2: ActionCreator<T2, Payload2>,
creator3: ActionCreator<T3, Payload3>,
): (o: Observable<Action<any>>) => Observable<ReturnType<typeof creator1>
| ReturnType<typeof creator2>
| ReturnType<typeof creator3>>;
// 2 action types
export function ofType<T extends string, Payload extends {}, T2 extends string, Payload2 extends {}, InputT>(
creator1: ActionCreator<T, Payload>,
creator2: ActionCreator<T2, Payload2>,
): (o: Observable<Action<any>>) => Observable<ReturnType<typeof creator1>|ReturnType<typeof creator2>>;
// 1 action type
export function ofType<T extends string, Payload extends {}>(
creator: ActionCreator<T, Payload>,
): (o: Observable<Action<any>>) => Observable<ReturnType<typeof creator>>;
export function ofType(...actionCreators: Array<ActionCreator<string, any>>): (o: Observable<any>) => Observable<any> {
const types = actionCreators.map(creator => creator.type);
return filter(action => types.includes(action.type));
}
import { ActionsUnion, createAction } from '../../../utils';
import { User } from './user.state';
export enum USER_ACTIONS {
SET_PROFILE = '[User] SET_PROFILE',
}
export const UserActions = {
setProfile: createAction(
USER_ACTIONS.SET_PROFILE,
(profile: User) => ({ profile }),
),
};
export type UserActions = ActionsUnion<typeof UserActions>;
import { ActionsObservable, combineEpics } from 'redux-observable';
import { of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { oftype } from 'ofType.operator.tsx';
import { UserActions } from './user.actions';
type Action$ = ActionsObservable<UserActions>;
export const setProfile$ = (action$: Action$) => action$.pipe(
ofType(UserActions.setProfile),
mergeMap(async ({ profile }) => {
// Do something...
return { type: '?' }; // Map result to another action
}),
);
export const epics = combineEpics<any>(
setProfile$,
);
// Sample reducer made using the utils
export const reducer = createPersistedReducer<UserState, UserActions>(
(state = initialState, action): UserState => {
switch (action.type) {
case USER_ACTIONS.SET_PROFILE:
return {
...state,
profile: defaultTo(state.profile)(action.profile),
};
default:
return state;
}
},
)({
key: 'user',
blacklist: ['getUserQuery'],
});
import { PersistedState } from 'redux-persist';
export const GET_USER_QUERY = 'getUserQuery';
export interface User {
id: string;
firstName: string;
lastName: string;
email: string;
};
export interface UserState extends PersistedState {
profile?: User;
getUserQuery: Query<User>; // Custom type for handling queries
}
export const initialState: UserState = {
profile: undefined,
getUserQuery: {},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment