Last active
October 4, 2018 11:44
-
-
Save JasperH8g/db8b286d3b881d489afdc06841ade55d to your computer and use it in GitHub Desktop.
This is how I like to do Redux stuff in TypeScript. Good coverage, with minimal effort and redefining types. What do you think?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Types | |
*/ | |
// IState | |
import rootReducer from "./reducers"; | |
export type IState = { | |
// Maps the return type of each reducer into its return-type | |
[K in keyof typeof rootReducer]: ReturnType<typeof rootReducer[K]> | |
} | |
// Action | |
import { MagicNumberActionTypes } from "./magic-number-actions"; | |
// imports of more action types... | |
export type Action = | |
| MagicNumberActionTypes | |
// more action types... | |
// Dispatch: could potentially call action-creators that are thunked, and thus need state (our state) | |
import { Dispatch as ReduxDispatch } from "redux"; | |
export type Dispatch = ReduxDispatch<IState>; | |
// ActionCreator types | |
import { ActionCreator } from "redux"; | |
import { ThunkAction } from "redux-thunk"; | |
// Type of an action creator that is a thunk, but doesn't return a promise | |
export type SyncThunkActionCreator<ReturnType> = ActionCreator<ThunkAction<ReturnType, IState, void>>; | |
// Type of an action creator that is a thunk that returns a promise | |
export type AsyncThunkActionCreator<ReturnType> = ActionCreator<ThunkAction<Promise<ReturnType>, IState, void>>; | |
/** | |
* Reducers | |
*/ | |
// ./reducers/index.ts | |
import magicNumber from "./magic-number-reducer"; | |
// imports of more reducers... | |
const rootReducer = { | |
magicNumber, | |
// more properties with reducers... | |
}; | |
export default rootReducer; | |
// ./reducers/magic-number-reducer.ts | |
import { Action } from "../types"; | |
type State = number | null; | |
export default function(state: State = null, action: Action): State { | |
switch (action.type) { | |
case "MAGIC_NUMBER_SUCCESS": | |
return action.magicNumber; | |
case "MAGIC_NUMBER_REQUEST": | |
case "MAGIC_NUMBER_FAILURE": | |
return null; | |
default: | |
return state; | |
} | |
} | |
/** | |
* Action creators | |
*/ | |
// ./actions/magic-number-actions.ts | |
import { AsyncThunkActionCreator } from "../types"; | |
export const getMagicNumber: AsyncThunkActionCreator<void> = () => { | |
return async (dispatch) => { | |
dispatch(magicNumberRequest()); | |
try { | |
const magicNumber = await MagicNumberApi.get(); | |
dispatch(magicNumberSuccess(magicNumber)); | |
} catch (error) { | |
dispatch(magicNumberFailure(error)); | |
} | |
}; | |
}; | |
// Plain action creators that are used above | |
function magicNumberRequest(): {type: "MAGIC_NUMBER_REQUEST"} { | |
return {type: "MAGIC_NUMBER_REQUEST"}; | |
} | |
function magicNumberSuccess(payload: number): {type: "MAGIC_NUMBER_SUCCESS", magicNumber: number} { | |
return {type: "MAGIC_NUMBER_SUCCESS", magicNumber}; | |
} | |
function magicNumberFailure(error: HttpError): {type: "MAGIC_NUMBER_FAILURE", error: Error} { | |
return {type: "MAGIC_NUMBER_FAILURE", error}; | |
} | |
// Export all available types | |
export type MagicNumberActionTypes = | |
| ReturnType<typeof magicNumberRequest> | |
| ReturnType<typeof magicNumberSuccess> | |
| ReturnType<typeof magicNumberFailure> | |
; | |
/** | |
* Container components | |
*/ | |
// ./components/MagicNumber/MagicNumberContainer.ts | |
import { connect } from "react-redux"; | |
import { IState, Dispatch } from "../types"; | |
import { GetMagicNumber } from "../actions/magic-number-actions"; | |
import MagicNumber from "./MagicNumber"; | |
interface IStateProps { | |
magicNumber: IState["magicNumber"]; | |
} | |
const mapStateToProps = (state: IState): IStateProps => ({ | |
magicNumber: state.magicNumber, | |
}); | |
interface IDispatchProps { | |
getMagicNumber: () => void; | |
} | |
const mapDispatchToProps = (dispatch: Dispatch): IDispatchProps => ({ | |
getMagicNumber: () => dispatch(getMagicNumber()), | |
}); | |
export default connect(mapStateToProps, mapDispatchToProps)(MagicNumber); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment