diff --git a/package.json b/package.json index 95799d6..2b18121 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "redux": "^4.0.1", "redux-observable": "^1.0.0", "rxjs": "^6.4.0", + "typesafe-actions": "^3.0.0", "typescript": "3.2.4" }, "scripts": { diff --git a/src/components/WorkloadList/WorkloadList.tsx b/src/components/WorkloadList/WorkloadList.tsx index f77ac91..cf0661e 100644 --- a/src/components/WorkloadList/WorkloadList.tsx +++ b/src/components/WorkloadList/WorkloadList.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Dispatch } from 'redux'; import { connect } from 'react-redux'; -import { Action, State } from '../../state'; +import { RootAction, RootState } from '../../state'; import { cancel } from '../../state/workloads/actions'; import { WorkloadItem, WorkloadItemStateProps } from '../WorkloadItem'; @@ -36,11 +36,11 @@ const WorkloadList: React.SFC = ({ workloads, cancelWorkload ); -const mapStateToProps = (state: State): WorkloadListStateProps => ({ +const mapStateToProps = (state: RootState): WorkloadListStateProps => ({ workloads: Object.values(state.workloads), }); -const mapDispatchToProps = (dispatch: Dispatch): WorkloadListDispatchProps => ({ +const mapDispatchToProps = (dispatch: Dispatch): WorkloadListDispatchProps => ({ cancelWorkload: (id: number) => dispatch(cancel({ id })), }) diff --git a/src/index.tsx b/src/index.tsx index f2f7174..69c7da7 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,7 +5,7 @@ import { Provider } from 'react-redux'; import { createEpicMiddleware } from 'redux-observable'; import moment from 'moment'; -import { reducer, epics, Action, State } from './state'; +import { reducer, epics, RootAction, RootState } from './state'; import * as WorkloadActions from './state/workloads/actions'; import './index.css'; import App from './components/App'; @@ -14,7 +14,7 @@ import App from './components/App'; // @ts-ignore: use Redux devtools if installed in browser const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; -const epicMiddleware = createEpicMiddleware(); +const epicMiddleware = createEpicMiddleware(); const store = createStore(reducer, composeEnhancers(applyMiddleware(epicMiddleware))); epicMiddleware.run(epics); diff --git a/src/state/actions.ts b/src/state/actions.ts deleted file mode 100644 index c38fe9f..0000000 --- a/src/state/actions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as WorkloadsActions from './workloads/actions'; -import { Action as WorkloadsAction } from './workloads/actions'; - -export type Action = WorkloadsAction; - -export const Actions = { - WorkloadsActions, -}; - -export default Actions; diff --git a/src/state/epics.ts b/src/state/epics.ts index 4cc1d7d..3935d80 100644 --- a/src/state/epics.ts +++ b/src/state/epics.ts @@ -4,4 +4,4 @@ import { epics as workloadsEpics } from './workloads'; export const epics = combineEpics(workloadsEpics); -export default epics; \ No newline at end of file +export default epics; diff --git a/src/state/index.ts b/src/state/index.ts index 4982eb3..bae8d18 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -1,3 +1,2 @@ export * from './reducer'; -export * from './actions'; -export * from './epics'; \ No newline at end of file +export * from './epics'; diff --git a/src/state/reducer.ts b/src/state/reducer.ts index e77ee0a..209dfe7 100644 --- a/src/state/reducer.ts +++ b/src/state/reducer.ts @@ -1,17 +1,16 @@ import { combineReducers } from 'redux'; -import { Action } from './actions'; - -import { - State as WorkloadsState, - reducer as workloadReducer, -} from './workloads'; +import { WorkloadsAction, WorkloadsState, workloadReducer } from './workloads'; -export interface State { +export type RootAction = + | WorkloadsAction; + +export interface RootState { workloads: WorkloadsState; } -export const reducer = combineReducers({ + +export const reducer = combineReducers({ workloads: workloadReducer, }); diff --git a/src/state/workloads/actions.ts b/src/state/workloads/actions.ts index ce12ea0..f555829 100644 --- a/src/state/workloads/actions.ts +++ b/src/state/workloads/actions.ts @@ -1,60 +1,21 @@ +import { createAction } from 'typesafe-actions'; + import { Status } from './types'; +import { SUBMIT, CREATED, CANCEL, UPDATE_STATUS } from './constants'; -export type Action = { - type: 'WORKLOAD_SUBMIT'; - payload: { - complexity: number; - }; -} | { - type: 'WORKLOAD_CREATED'; - payload: { - id: number; - complexity: number; - completeDate: Date; - status: Status; - }; -} | { - type: 'WORKLOAD_CANCEL'; - payload: { - id: number; - }; -} | { - type: 'WORKLOAD_UPDATE_STATUS'; - payload: { - id: number; - status: Status; - }; -}; -export const submit = ({ complexity }: { complexity: number }): Action => ({ - type: 'WORKLOAD_SUBMIT', - payload: { - complexity, - }, -}); +export const submit = createAction(SUBMIT, resolve => (params: { complexity: number }) => resolve({ complexity: params.complexity })); -export const created = ({ id, status, complexity, completeDate }: { id: number, status: Status, complexity: number, completeDate: Date }): Action => ({ - type: 'WORKLOAD_CREATED', - payload: { - id, - status, - completeDate, - complexity, - }, -}); +export const created = createAction(CREATED, resolve => + (params: { id: number, status: Status, complexity: number, completeDate: Date }) => resolve({ + id: params.id, + status: params.status, + completeDate: params.completeDate, + complexity: params.complexity, + })); -export const cancel = ({ id }: { id: number }): Action => ({ - type: 'WORKLOAD_CANCEL', - payload: { - id, - }, -}); +export const cancel = createAction(CANCEL, resolve => (params: { id: number }) => resolve({ id: params.id })); -export const updateStatus = ({ id, status }: { id: number, status: Status }): Action => ({ - type: 'WORKLOAD_UPDATE_STATUS', - payload: { - id, - status, - }, -}); +export const updateStatus = createAction(UPDATE_STATUS, resolve => + (params: { id: number, status: Status }) => resolve({ id: params.id, status: params.status })) diff --git a/src/state/workloads/constants.ts b/src/state/workloads/constants.ts new file mode 100644 index 0000000..3b5155a --- /dev/null +++ b/src/state/workloads/constants.ts @@ -0,0 +1,4 @@ +export const SUBMIT = 'workload/SUBMIT'; +export const CREATED = 'workload/CREATED'; +export const CANCEL = 'workload/CANCEL'; +export const UPDATE_STATUS = 'workload/UPDATE_STATUS'; diff --git a/src/state/workloads/epics.ts b/src/state/workloads/epics.ts index 15f03f6..06d053f 100644 --- a/src/state/workloads/epics.ts +++ b/src/state/workloads/epics.ts @@ -1,11 +1,12 @@ import { combineEpics, Epic, ofType } from 'redux-observable'; -import { map, tap, ignoreElements } from 'rxjs/operators'; +import { filter, map, tap, ignoreElements } from 'rxjs/operators'; +import { isActionOf } from 'typesafe-actions'; -import { Action } from '../actions'; -import { State } from '../reducer'; +import { RootAction, RootState } from '../reducer'; +import * as workloadsActions from './actions'; -type AppEpic = Epic; +type AppEpic = Epic; // import { WorkloadService } from './services'; @@ -16,7 +17,7 @@ type AppEpic = Epic; const logWorkloadSubmissions: AppEpic = (action$, state$) => ( action$.pipe( - ofType('WORKLOAD_SUBMIT'), + filter(isActionOf(workloadsActions.submit)), map(action => action.payload), tap((payload) => console.log('Workload submitted', payload)), ignoreElements(), diff --git a/src/state/workloads/reducer.ts b/src/state/workloads/reducer.ts index fe0f6d8..f6f121f 100644 --- a/src/state/workloads/reducer.ts +++ b/src/state/workloads/reducer.ts @@ -1,23 +1,29 @@ -import { Action } from './actions'; -import { Status } from './types'; +import { ActionType, getType } from 'typesafe-actions'; -interface Entry { +import { Status } from './types'; +import * as workloadActions from './actions'; + + +export type WorkloadsAction = ActionType + + +interface WorkloadEntry { id: Id; complexity: number; completeDate: Date; status: Status; } -export type State = { - [Id in number]: Entry; +export type WorkloadsState = { + [Id in number]: WorkloadEntry; }; -const initialState: State = {}; +const initialState: WorkloadsState = {}; -export const reducer = (state: State = initialState, action: Action): State => { +export const workloadReducer = (state: WorkloadsState = initialState, action: WorkloadsAction): WorkloadsState => { switch (action.type) { - case 'WORKLOAD_CREATED': + case getType(workloadActions.created): return { ...state, [action.payload.id]: { @@ -28,7 +34,7 @@ export const reducer = (state: State = initialState, action: Action): State => { }, }; - case 'WORKLOAD_UPDATE_STATUS': + case getType(workloadActions.updateStatus): return { ...state, [action.payload.id]: { diff --git a/yarn.lock b/yarn.lock index b29a780..9b43c9f 100755 --- a/yarn.lock +++ b/yarn.lock @@ -9431,6 +9431,11 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typesafe-actions@^3.0.0: + version "3.0.0" + resolved "https://npm.nutterlogic.com/typesafe-actions/-/typesafe-actions-3.0.0.tgz#8b162ea11fa7383d4b4d96afc181118e84e06e10" + integrity sha512-NLpRc/FY+jPfWL0aUXQzjxPyF0Xug2om6akaoRLQ18KGwP2yYNBJu9vkv2q1q+Cx/+edy2Qf6O8xXnYY/xwz1A== + typescript@3.2.4: version "3.2.4" resolved "https://npm.nutterlogic.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"