Created
April 4, 2018 11:13
-
-
Save brandonpapworth/b8b268707f6d7e27bc8d381d21db4e86 to your computer and use it in GitHub Desktop.
Using class-based action types instead of strings in Reduxsauce
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
'use strict'; | |
const __ACTION_TYPE_NAME__ = Symbol('ActionType[__ACTION_TYPE_NAME__]'); | |
const actionTypeSingletonStore = new Map(); | |
export class ActionType { | |
constructor(actionTypeName, ignoreActionTypeSingletonStore = false) { | |
if (ignoreActionTypeSingletonStore === false) { | |
let actionTypeInstance = actionTypeSingletonStore.get(actionTypeName); | |
if (actionTypeInstance === undefined) { | |
actionTypeSingletonStore.set(actionTypeName, this); | |
} else { | |
return this; | |
} | |
} | |
this[__ACTION_TYPE_NAME__] = actionTypeName; | |
} | |
toString() { | |
return this[__ACTION_TYPE_NAME__]; | |
} | |
toJSON() { | |
return this[__ACTION_TYPE_NAME__]; | |
} | |
} |
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
'use strict'; | |
/** | |
* This replaces the `createActions` library function of reduxsauce to allow for | |
* class-based types to be used. | |
* https://github.com/infinitered/reduxsauce/blob/master/lib/createActions.js | |
* [specific time referenced] https://github.com/infinitered/reduxsauce/blob/7735e5129183fe8a76b233405ecbc2fdb5ddd76b/lib/createActions.js | |
*/ | |
import { is, isNil, isEmpty, join, keys, map, mapObjIndexed, merge, pick, pipe, replace, toUpper, zipObj } from 'ramda'; | |
import { ActionType } from './action-type.class'; | |
import { createTypes } from './create-types.class-based'; | |
const DEFAULT_OPTIONS = { | |
prefix: '' | |
}; | |
const RX_CAPS = /(?!^)([A-Z])/g; | |
const camelToScreamingSnake = pipe( | |
replace(RX_CAPS, '_$1'), | |
toUpper | |
); | |
function convertToTypes(config, options) { | |
const opts = merge(DEFAULT_OPTIONS, options); | |
return pipe( | |
keys, | |
map(camelToScreamingSnake), | |
join(' '), | |
types => createTypes(types, opts) | |
)(config); | |
} | |
function createActionCreatorWithOnlyType(type) { | |
return function createAction() { return { type }; }; | |
} | |
function createActionCreatorWithArrayOfPropNames(type, propNames) { | |
return function createAction(...values) { | |
var extraProps = zipObj(propNames, values); | |
return { type, ...extraProps }; | |
}; | |
} | |
function createActionCreatorWithDefaultProps(type, defaultProps) { | |
const defaultPropKeys = Object.keys(defaultProps); | |
return function createAction(valueObject) { | |
const providedProps = pick(defaultPropKeys, valueObject); | |
return { type, ...defaultProps, ...providedProps }; | |
}; | |
} | |
function createActionCreator(name, extraPropNames, options) { | |
const { prefix } = merge(DEFAULT_OPTIONS, options); | |
const type = new ActionType(`${prefix}${camelToScreamingSnake(name)}`); | |
const noKeys = isNil(extraPropNames) || isEmpty(extraPropNames); | |
let actionCreator = undefined; | |
if (noKeys) { | |
actionCreator = createActionCreatorWithOnlyType(type); | |
} else if (is(Array, extraPropNames)) { | |
actionCreator = createActionCreatorWithArrayOfPropNames(type, extraPropNames); | |
} else if (is(Object, extraPropNames)) { | |
actionCreator = createActionCreatorWithDefaultProps(type, extraPropNames); | |
} else { | |
throw new Error('action props must be a null/array/object/function'); | |
} | |
return actionCreator; | |
} | |
function convertToCreators(config, options) { | |
return mapObjIndexed((num, key, value) => { | |
if (typeof value[key] === 'function') { | |
return value[key]; | |
} else { | |
return createActionCreator(key, value[key], options); | |
} | |
})(config); | |
} | |
export default function createActions(config, options) { | |
if (isNil(config)) { | |
throw new Error('an object is required to setup types and creators') | |
} | |
if (isEmpty(config)) { | |
throw new Error('empty objects are not supported') | |
} | |
return { | |
Types: convertToTypes(config, options), | |
Creators: convertToCreators(config, options) | |
}; | |
} |
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
'use strict'; | |
import { createTypes as reduxsauceCreateTypes } from 'reduxsauce'; | |
import { ActionType } from './action-type.class'; | |
export createTypes(...args) { | |
return Object | |
.entries(reduxsauceCreateTypes(...args)) | |
.reduce((typesObject, [typeName, prefixedTypeName]) => { | |
typesObject[typeName] = new ActionType(prefixedTypeName); | |
return typesObject; | |
}, {}); | |
; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment