Skip to content

Instantly share code, notes, and snippets.

@xhjkl
Last active December 21, 2017 07:03
Show Gist options
  • Save xhjkl/16cafc249dfa66e3820f995c2495f354 to your computer and use it in GitHub Desktop.
Save xhjkl/16cafc249dfa66e3820f995c2495f354 to your computer and use it in GitHub Desktop.
Action creators acting as action types
//
// Study on how one could use action creators instead of string literals denoting action types.
//
// Usually, in most Redux workflows, one would define string literals to distinguish action types:
export const USER_DID_LOG_IN = 'USER_DID_LOG_IN'
// It hurts performance a little, because comparing strings might be slower than comparing numbers or object identities.
// It also hinders maintenance, as now the maintainer has to update twice as much code should the action name change.
// A very simple solution to both of these issues would be using a closure instead of a string:
export const USER_DID_LOG_IN = () => {}
// With that approach, all the exported ids remain unique, as different closures compare unequal,
// and the maintainer does not have to duplicate the effort.
//
// It might appear that we lose the prettiness of text name, but actually NodeJS
// and recent browsers are smart enough to deduce closure name based on the reference it is associated to:
export const USER_DID_LOG_IN = () => {}
console.log(USER_DID_LOG_IN) // => [Function: USER_DID_LOG_IN]
// All that means using closures as the action identifiers is ultimately better.
//
// Also note that with each action type constant is usually an accompanying action creator:
export const userDidLogIn = () => ({ type: USER_DID_LOG_IN })
// Actually, one could use just that action creator for identifying the action being created:
export const userDidLogIn = () => ({ type: userDidLogIn })
// However, this still requires double typing. We could automate it with an Y-combinator:
const action = (fn: ActionType) => {
let actionKind
actionKind = (...rest: any) => fn(actionKind, ...rest)
return actionKind
}
export const userDidLogIn = action(
(type) => ({ type })
)
// It might look convoluted at first, but what it does is just binding the function to the first argument
// being that function itself.
//
// In the above example, in the action creator body, `type === userDidLogIn`.
//
// We cannot use `userDidLogIn.bind(null, userDidLogIn)` because it shall return another function,
// unequal to the original.
//
// On the side of reducers, this approach also reduces cognitive strain: instead of choosing
// between `ActionType.USER_DID_LOGIN` and `ActionType.userDidLogIn`, it is always the second form.
//
// It is also arguably prettier, because JS usually never uses `SHOUTING_CASING` elsewhere but in Redux:
import * as ActionType from './actions'
const userIsLoggedIn = (state = false, action) {
switch (action.type) {
case ActionType.userDidLogIn:
return true
default:
return state
}
}
// All in all, there is an opinion that the string literals acting as action types
// should be eliminated altogether, and the action creators should be used in their place.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment