Upgrade action typing with typesafe-actions
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"redux-observable": "^1.0.0",
|
"redux-observable": "^1.0.0",
|
||||||
"rxjs": "^6.4.0",
|
"rxjs": "^6.4.0",
|
||||||
|
"typesafe-actions": "^3.0.0",
|
||||||
"typescript": "3.2.4"
|
"typescript": "3.2.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Action, State } from '../../state';
|
import { RootAction, RootState } from '../../state';
|
||||||
import { cancel } from '../../state/workloads/actions';
|
import { cancel } from '../../state/workloads/actions';
|
||||||
import { WorkloadItem, WorkloadItemStateProps } from '../WorkloadItem';
|
import { WorkloadItem, WorkloadItemStateProps } from '../WorkloadItem';
|
||||||
|
|
||||||
@@ -36,11 +36,11 @@ const WorkloadList: React.SFC<WorkloadListProps> = ({ workloads, cancelWorkload
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = (state: State): WorkloadListStateProps => ({
|
const mapStateToProps = (state: RootState): WorkloadListStateProps => ({
|
||||||
workloads: Object.values(state.workloads),
|
workloads: Object.values(state.workloads),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch<Action>): WorkloadListDispatchProps => ({
|
const mapDispatchToProps = (dispatch: Dispatch<RootAction>): WorkloadListDispatchProps => ({
|
||||||
cancelWorkload: (id: number) => dispatch(cancel({ id })),
|
cancelWorkload: (id: number) => dispatch(cancel({ id })),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Provider } from 'react-redux';
|
|||||||
import { createEpicMiddleware } from 'redux-observable';
|
import { createEpicMiddleware } from 'redux-observable';
|
||||||
import moment from 'moment';
|
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 * as WorkloadActions from './state/workloads/actions';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
@@ -14,7 +14,7 @@ import App from './components/App';
|
|||||||
// @ts-ignore: use Redux devtools if installed in browser
|
// @ts-ignore: use Redux devtools if installed in browser
|
||||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||||
|
|
||||||
const epicMiddleware = createEpicMiddleware<Action, Action, State>();
|
const epicMiddleware = createEpicMiddleware<RootAction, RootAction, RootState>();
|
||||||
const store = createStore(reducer, composeEnhancers(applyMiddleware(epicMiddleware)));
|
const store = createStore(reducer, composeEnhancers(applyMiddleware(epicMiddleware)));
|
||||||
epicMiddleware.run(epics);
|
epicMiddleware.run(epics);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -4,4 +4,4 @@ import { epics as workloadsEpics } from './workloads';
|
|||||||
|
|
||||||
export const epics = combineEpics(workloadsEpics);
|
export const epics = combineEpics(workloadsEpics);
|
||||||
|
|
||||||
export default epics;
|
export default epics;
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './reducer';
|
export * from './reducer';
|
||||||
export * from './actions';
|
export * from './epics';
|
||||||
export * from './epics';
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
|
|
||||||
import { Action } from './actions';
|
import { WorkloadsAction, WorkloadsState, workloadReducer } from './workloads';
|
||||||
|
|
||||||
import {
|
|
||||||
State as WorkloadsState,
|
|
||||||
reducer as workloadReducer,
|
|
||||||
} from './workloads';
|
|
||||||
|
|
||||||
|
|
||||||
export interface State {
|
export type RootAction =
|
||||||
|
| WorkloadsAction;
|
||||||
|
|
||||||
|
export interface RootState {
|
||||||
workloads: WorkloadsState;
|
workloads: WorkloadsState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reducer = combineReducers<State, Action>({
|
|
||||||
|
export const reducer = combineReducers<RootState, RootAction>({
|
||||||
workloads: workloadReducer,
|
workloads: workloadReducer,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,60 +1,21 @@
|
|||||||
|
import { createAction } from 'typesafe-actions';
|
||||||
|
|
||||||
import { Status } from './types';
|
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 => ({
|
export const submit = createAction(SUBMIT, resolve => (params: { complexity: number }) => resolve({ complexity: params.complexity }));
|
||||||
type: 'WORKLOAD_SUBMIT',
|
|
||||||
payload: {
|
|
||||||
complexity,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const created = ({ id, status, complexity, completeDate }: { id: number, status: Status, complexity: number, completeDate: Date }): Action => ({
|
export const created = createAction(CREATED, resolve =>
|
||||||
type: 'WORKLOAD_CREATED',
|
(params: { id: number, status: Status, complexity: number, completeDate: Date }) => resolve({
|
||||||
payload: {
|
id: params.id,
|
||||||
id,
|
status: params.status,
|
||||||
status,
|
completeDate: params.completeDate,
|
||||||
completeDate,
|
complexity: params.complexity,
|
||||||
complexity,
|
}));
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const cancel = ({ id }: { id: number }): Action => ({
|
export const cancel = createAction(CANCEL, resolve => (params: { id: number }) => resolve({ id: params.id }));
|
||||||
type: 'WORKLOAD_CANCEL',
|
|
||||||
payload: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateStatus = ({ id, status }: { id: number, status: Status }): Action => ({
|
export const updateStatus = createAction(UPDATE_STATUS, resolve =>
|
||||||
type: 'WORKLOAD_UPDATE_STATUS',
|
(params: { id: number, status: Status }) => resolve({ id: params.id, status: params.status }))
|
||||||
payload: {
|
|
||||||
id,
|
|
||||||
status,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
4
src/state/workloads/constants.ts
Normal file
4
src/state/workloads/constants.ts
Normal file
@@ -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';
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { combineEpics, Epic, ofType } from 'redux-observable';
|
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 { RootAction, RootState } from '../reducer';
|
||||||
import { State } from '../reducer';
|
import * as workloadsActions from './actions';
|
||||||
|
|
||||||
|
|
||||||
type AppEpic = Epic<Action, Action, State>;
|
type AppEpic = Epic<RootAction, RootAction, RootState>;
|
||||||
|
|
||||||
|
|
||||||
// import { WorkloadService } from './services';
|
// import { WorkloadService } from './services';
|
||||||
@@ -16,7 +17,7 @@ type AppEpic = Epic<Action, Action, State>;
|
|||||||
|
|
||||||
const logWorkloadSubmissions: AppEpic = (action$, state$) => (
|
const logWorkloadSubmissions: AppEpic = (action$, state$) => (
|
||||||
action$.pipe(
|
action$.pipe(
|
||||||
ofType('WORKLOAD_SUBMIT'),
|
filter(isActionOf(workloadsActions.submit)),
|
||||||
map(action => action.payload),
|
map(action => action.payload),
|
||||||
tap((payload) => console.log('Workload submitted', payload)),
|
tap((payload) => console.log('Workload submitted', payload)),
|
||||||
ignoreElements(),
|
ignoreElements(),
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
import { Action } from './actions';
|
import { ActionType, getType } from 'typesafe-actions';
|
||||||
import { Status } from './types';
|
|
||||||
|
|
||||||
interface Entry<Id extends number> {
|
import { Status } from './types';
|
||||||
|
import * as workloadActions from './actions';
|
||||||
|
|
||||||
|
|
||||||
|
export type WorkloadsAction = ActionType<typeof workloadActions>
|
||||||
|
|
||||||
|
|
||||||
|
interface WorkloadEntry<Id extends number> {
|
||||||
id: Id;
|
id: Id;
|
||||||
complexity: number;
|
complexity: number;
|
||||||
completeDate: Date;
|
completeDate: Date;
|
||||||
status: Status;
|
status: Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type State = {
|
export type WorkloadsState = {
|
||||||
[Id in number]: Entry<Id>;
|
[Id in number]: WorkloadEntry<Id>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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) {
|
switch (action.type) {
|
||||||
case 'WORKLOAD_CREATED':
|
case getType(workloadActions.created):
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[action.payload.id]: {
|
[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 {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[action.payload.id]: {
|
[action.payload.id]: {
|
||||||
|
|||||||
@@ -9431,6 +9431,11 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
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:
|
typescript@3.2.4:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://npm.nutterlogic.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
|
resolved "https://npm.nutterlogic.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
|
||||||
|
|||||||
Reference in New Issue
Block a user