redux-tiles
Version:
Library to create and easily compose redux pieces together in less verbose manner
163 lines (147 loc) • 4.26 kB
text/typescript
import { Reducer } from "redux";
import { createType } from "../helpers";
import { asyncAction, createResetAction, syncAction } from "./actions";
import { createReducer } from "./reducer";
import { createSelectors } from "./selectors";
import {
IAsyncActionTypes,
ICreateSelectorsTypes,
IData,
IOverloadedAction,
ISelectors,
ISyncActionTypes,
ISyncTileParams,
ITile,
ITileParams,
ReducerObject,
SyncData
} from "./types";
const prefix: string = "Redux_Tiles_";
export interface ITypes {
[key: string]: string;
}
export interface IReducerAction {
payload: { data: any } | undefined;
error: string | Object | undefined | null;
}
export function createTile(params: ITileParams): ITile {
const { type, fn, caching, nesting, selectorFallback = null } = params;
// initial state is equal to empty object, because of possible nesting
// basically every object should contain default properties, so we handle
// this situation using selectors
const initialState: any = nesting ? {} : null;
const identificator: string = createType({ type });
const types: ITypes = {
START: `${prefix}${identificator}_START`,
SUCCESS: `${prefix}${identificator}_SUCCESS`,
FAILURE: `${prefix}${identificator}_FAILURE`,
RESET: `${prefix}${identificator}_RESET`
};
const selectorParams: ICreateSelectorsTypes = {
selectorFallback: {
isPending: false,
error: null,
data: selectorFallback,
fetched: false
},
tileName: type,
nesting
};
const selectors: ISelectors = createSelectors(selectorParams);
const actionParams: IAsyncActionTypes = {
START: types.START,
SUCCESS: types.SUCCESS,
FAILURE: types.FAILURE,
fn,
type,
caching,
nesting,
selectors
};
const action: IOverloadedAction = asyncAction(actionParams);
action.reset = createResetAction({ type: types.RESET });
const reducerObject: ReducerObject = {
[types.START]: {
data: null,
isPending: true,
error: null,
fetched: false
},
[types.FAILURE]: (_storeState: {}, storeAction: IReducerAction): IData => ({
data: null,
isPending: false,
error: storeAction.error,
fetched: true
}),
[types.SUCCESS]: (_storeState: {}, storeAction: IReducerAction): IData => ({
error: null,
isPending: false,
data: storeAction.payload && storeAction.payload.data,
fetched: true
}),
[types.RESET]: initialState
};
const reducer: Reducer<any> = createReducer(initialState, reducerObject);
return {
action,
reducer,
selectors,
tileName: type,
constants: types,
reflect: params
};
}
export function createSyncTile(params: ISyncTileParams): ITile {
const {
type,
nesting,
fn = (fnParams: any): any => fnParams.params,
fns,
// we have default state as an object because of the possible nesting
initialState = nesting ? {} : null,
selectorFallback
} = params;
const identificator: string = createType({ type });
const types: ITypes = {
SET: `${prefix}${identificator}_SET`,
RESET: `${prefix}${identificator}_RESET`
};
const selectorParams: ICreateSelectorsTypes = {
selectorFallback,
tileName: type,
nesting
};
const selectors: ISelectors = createSelectors(selectorParams);
const actionParams: ISyncActionTypes = {
SET: types.SET,
nesting,
fn,
selector: selectors.get
};
const action: IOverloadedAction = syncAction(actionParams);
action.reset = createResetAction({ type: types.RESET });
if (fns) {
Object.keys(fns).forEach((methodName: string) => {
const method: any = fns[methodName];
const customActionParams: ISyncActionTypes = {
...actionParams,
fn: method
};
action[methodName] = syncAction(customActionParams);
});
}
const reducerObject: ReducerObject = {
[types.SET]: (_storeState: {}, storeAction: IReducerAction): SyncData =>
storeAction.payload && storeAction.payload.data,
[types.RESET]: initialState
};
const reducer: Reducer<any> = createReducer(initialState, reducerObject);
return {
action,
selectors,
reducer,
tileName: type,
constants: types,
reflect: params
};
}