Last active
June 8, 2017 14:51
-
-
Save T4rk1n/8e23f19790b6d52126f446d532a06202 to your computer and use it in GitHub Desktop.
Redux store implementation with thunk and promise middleware.
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
export const fulfilled = (actionType) => `${actionType}_FULFILLED` | |
export const rejected = (actionType) => `${actionType}_REJECTED` | |
export const pending = (actionType) => `${actionType}_PENDING` | |
export const resetAction = (actionType) => `${actionType}_RESET` | |
/** | |
* Create a dictionary with all the phases of an async action | |
* @param {string} baseAction | |
*/ | |
const createActionTypes = (baseAction) => ({ | |
base: baseAction, | |
pending: pending(baseAction), | |
fulfilled: fulfilled(baseAction), | |
rejected: rejected(baseAction), | |
reset: resetAction(baseAction) | |
}) | |
const initialState = { | |
pending: false, | |
fulfilled: false, | |
rejected: false, | |
result: null, | |
error: null | |
} | |
/** | |
* Simple callbacks options to not fail because they are always callable | |
* @type {{onSuccess: {function}, onError: {function}}} | |
*/ | |
export const defaultActionOptions = { | |
onSuccess: (v) => {}, | |
onError: (v) => {} | |
} | |
/** | |
* Create an action with a reducer for each of the state of the promise. | |
* Designed for use with `redux-promise-middleware` | |
*/ | |
export class BaseAction { | |
constructor(actionType) { | |
this.actionTypes = createActionTypes(actionType) | |
this.actType = actionType | |
/** | |
* Simple reducer for promises states. | |
* @param state | |
* @param action | |
* @returns {*} | |
*/ | |
this.reducer = (state=initialState, action) => { | |
const { type, payload } = action | |
switch(type) { | |
case this.actionTypes.pending: | |
return {...state, pending: true} | |
case this.actionTypes.fulfilled: | |
return {...state, pending: false, fulfilled: true, result: payload} | |
case this.actionTypes.rejected: | |
return {...state, pending: false, error: payload, rejected: true} | |
case this.actionTypes.reset: | |
return initialState | |
default: | |
return state | |
} | |
} | |
// Bindings | |
this.act = this.act.bind(this) | |
} | |
/** | |
* The action to return as payload. | |
* @param p - params to overload. | |
* @returns {Promise.<void>} | |
*/ | |
async act(options=defaultActionOptions) { | |
throw new Error('Abstract call') | |
} | |
} | |
const defaultFetchConstructorOptions = { | |
requestOptions: { | |
method: 'get', | |
headers: new Headers({'Content-Type': 'application/json'}), | |
mode: 'cors' | |
}, | |
baseUrl: null, | |
hasFormatUrl: false | |
} | |
const defaultFetchActionOptions = { | |
...defaultActionOptions, | |
toFormat: '' | |
} | |
export class FetchAction extends BaseAction { | |
constructor(actionType, url, fetchOptions=defaultFetchConstructorOptions) { | |
super(actionType) | |
this.url = url | |
this.fetchOptions = {...defaultFetchConstructorOptions, ...fetchOptions} | |
} | |
async act(options=defaultFetchActionOptions) { | |
let results = {} | |
const { onSuccess, onError, toFormat } = {...defaultFetchActionOptions, ...options} | |
const { requestOptions, baseUrl, hasFormatUrl } = this.fetchOptions | |
let error | |
await fetch(`${baseUrl ? baseUrl :''}/${hasFormatUrl ? this.url.formatObject({toFormat}) : this.url}`, requestOptions) | |
.then(async value => { | |
results = await value.json() | |
onSuccess(results) | |
}) | |
.catch(async err => { | |
error = err | |
onError(err) | |
}) | |
if (error) throw error | |
return results | |
} | |
} |
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
import { applyMiddleware, createStore, combineReducers } from 'redux' | |
import thunk from 'redux-thunk' | |
import promiseMiddleware from 'redux-promise-middleware' | |
/** | |
* Hold the redux store and all the actions. | |
* | |
* @export | |
* @class ReduxStore | |
*/ | |
export class ReduxStore { | |
/** | |
* Create an instance of a redux store with a reducer for each actions. | |
* The end result of the will be in the store as the name of the action -> result | |
* | |
* @param {{...BaseAction}} actions - the actions to return as payload in dispatch | |
* @param {{}} extraReducers | |
*/ | |
constructor(actions, extraReducers={}) { | |
// Bindings | |
this.dispatch = this.dispatch.bind(this) | |
this.getState = this.getState.bind(this) | |
const reducers = Object.keys(actions) | |
.filter(a => actions.hasOwnProperty(a)) | |
.map(a => [a, actions[a].reducer]) | |
.reduce((o, [k,v]) => {o[k] = v; return o}, {}) | |
this.actions = Object.keys(actions) | |
.filter(a => actions.hasOwnProperty(a)) | |
.map(a => [a, (p) => this.dispatch(actions[a], p)]) | |
.reduce((o, [k,v]) => {o[k] = v; return o}, {}) | |
this._store = createStore(combineReducers({...reducers, ...extraReducers}), applyMiddleware(promiseMiddleware(), thunk)) | |
} | |
/** | |
* | |
* @param {BaseAction} action | |
* @param {{}} options | |
*/ | |
dispatch(action, options={}) { | |
this._store.dispatch({ | |
type: action.actionTypes.base, | |
payload: action.act(options) | |
}) | |
} | |
getState() { | |
return this._store.getState() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment