Last active
April 6, 2016 16:21
-
-
Save jgoux/d7fdf22d63fe27ca8103 to your computer and use it in GitHub Desktop.
redux-loop helpers
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 { loop, Effects as E, liftState } from "redux-loop" | |
import { | |
cond, pathEq, map, T, reduce, toUpper, pipe, transpose, | |
lensProp, set as lensSet, view as lensView | |
} from "ramda" | |
export const mkInit = (f) => pipe(f, liftState) | |
export const mkActionType = (namespace) => (type) => `${namespace}/${type}` | |
export const mkAction = (type, payload={}) => ({ type, payload }) | |
// TODO: Research a better API | |
export const mkUpdate = (cases = []) => (model, action, parentAction=null) => { | |
const actionEquals = ([actionType, f]) => [pathEq(["action", "type"], actionType), f] | |
return pipe( | |
cond([ | |
...(map(actionEquals)(cases)), | |
[T, ({ model }) => model] | |
]), | |
liftState | |
)({ model, action, parentAction }) | |
} | |
export const composeUpdates = (updates) => (model, action) => { | |
const [reducedModel, reducedEffect] = reduce( | |
([model, effect], update) => { | |
const [m, e] = update(model, action) | |
return [m, [ ...effect, e]] | |
}, | |
[model, []], | |
updates | |
) | |
return loop(reducedModel, E.batch(reducedEffect)) | |
} | |
// TODO: Remove a bit of magic here, make it more flexible | |
export const mkChild = (namespace) => (name, childUpdate) => { | |
const childLens = lensProp(name) | |
const actionType = mkActionType(namespace)(`MODIFY_${toUpper(name)}`) | |
const action = (a) => mkAction(actionType, { action: a }) | |
const update = mkUpdate([ | |
[ | |
actionType, | |
({ model, action: a }) => { | |
const { payload: { action: hoa } } = a | |
const [m, e] = childUpdate(lensView(childLens, model), hoa) | |
return loop( | |
lensSet(childLens, m, model), | |
E.lift(e, action) | |
) | |
} | |
] | |
]) | |
const forwardTo = (dispatch) => (hoa) => dispatch(action(hoa)) | |
return { actionType, action, update, forwardTo } | |
} | |
// TODO: Remove a bit of magic here, make it more flexible | |
export const manageChild = ({ actionType, update }) => (subUpdate) => (model, action) => { | |
const [m, e] = update(model, action) | |
if (action.type === actionType) { | |
const { payload: { action: hoa } } = action | |
const [sm, se] = subUpdate(m, hoa, action) | |
return loop( | |
sm, | |
E.batch([e, se]) | |
) | |
} | |
return loop(m, e) | |
} | |
// TODO: Remove a bit of magic here, make it more flexible | |
export const mkListOf = (namespace) => (name, childUpdate) => { | |
const listLens = lensProp("list") | |
const actionType = mkActionType(namespace)(`MODIFY_LIST`) | |
const action = (id, a) => mkAction(actionType, { id, action: a }) | |
const update = mkUpdate([ | |
[ | |
actionType, | |
({ model, action: { payload: { id, action: hoa } } }) => { | |
const loopList = map((item) => { | |
if (item.id !== id) { | |
return loop(item, E.none()) | |
} | |
const [data, e] = childUpdate(item.data, hoa) | |
return loop( | |
{ id, data }, | |
E.lift(e, action, id) | |
) | |
})(lensView(listLens, model)) | |
const [ms, es] = transpose(loopList) | |
return loop( | |
lensSet(listLens, ms, model), | |
E.batch(es) | |
) | |
} | |
] | |
]) | |
const forwardTo = (dispatch) => (id) => (hoa) => dispatch(action(id, hoa)) | |
return { actionType, action, update, forwardTo } | |
} |
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
// Usage of the helpers | |
import { loop, Effects as E } from "redux-loop" | |
import { find, allPass, propEq, pathEq, not, pipe, ifElse, isNil, always } from "ramda" | |
import { mkActionType, mkAction, mkUpdate, mkListOf, manageChild } from "helpers" | |
import { initialModel } from "./model" | |
import * as DemandeArchivee from "./components/DemandeArchivee" | |
const NAMESPACE = "DemandeArchiveeList" | |
const mkIsolatedListOf = mkListOf(NAMESPACE) | |
const children = { | |
list: mkIsolatedListOf("demandeArchivee", DemandeArchivee.update) | |
} | |
export const forwardTo = { | |
list: children.list.forwardTo | |
} | |
export const actionTypes = { | |
MODIFY_LIST: children.list.actionType | |
} | |
export const actions = { | |
modifyList: children.list.action | |
} | |
export const update = (model = initialModel, action) => | |
manageChild(children.list)(manageList)(model, action) | |
// Manage = Change the parent's state based on the child's actions | |
const manageList = mkUpdate([ | |
[ | |
DemandeArchivee.actionTypes.EXPAND, | |
({ model, parentAction: { payload: { id } } }) => { | |
const collapsable = allPass([ | |
pipe(propEq("id", id), not), | |
pathEq(["data", "isExpanded"], true) | |
]) | |
return pipe( | |
find(collapsable), | |
ifElse( | |
isNil, | |
always(model), | |
(item) => loop( | |
model, | |
E.constant(actions.modifyList(item.id, DemandeArchivee.actions.collapse())) | |
) | |
) | |
)(model.list) | |
} | |
] | |
]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment