Skip to content

Instantly share code, notes, and snippets.

@T4rk1n
Last active June 8, 2017 14:51
Show Gist options
  • Save T4rk1n/8e23f19790b6d52126f446d532a06202 to your computer and use it in GitHub Desktop.
Save T4rk1n/8e23f19790b6d52126f446d532a06202 to your computer and use it in GitHub Desktop.
Redux store implementation with thunk and promise middleware.
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
}
}
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