erikras / erikras/multireducer

architecture comment

Open
#123 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
421
Forks
23
PR merge metrics
No merged PRs in 30d

Description

Thought you might want to consider this:
The solution I've settled on for this kind of thing in my own projects is to use the same reducer and action creator code, but with action types prefixed differently for each reducer mount point, as depicted in the code below. I would love if `redux-form` worked this way.

It took a long long time before I had the idea to do things this way but once I did I wish I had thought of it at the very beginning.

Advantages:
* it's easier to read the action log since actions for different mount points have different types
* more flexible with mounting reducers in arbitrary, deeper-nested areas of the state
* you can move around where reducers are mounted without having to change state paths used to modify reducers or action creators

Disadvantages:
* it may not be obvious from the action type what part of the state it affects, depending on what action type prefixes you use

I even created `prefixReducer` and `prefixActionCreator` functions in my `mindfront-redux-utils` package to decorate existing reducers and action creators that weren't designed to be created with an action type prefix option.

```js
import {createReducer, combineReducers} from 'mindfront-redux-utils' // or redux-actions etc
import {List} from 'immutable'

const PUSH = 'PUSH'
const POP = 'POP'

function listActions(actionTypePrefix) {
const pushType = actionTypePrefix + PUSH
const popType = actionTypePrefix + POP
return {
push: (item) => ({type: pushType, payload: item}),
pop: () => ({type: popType}),
}
})

function listReducer(actionTypePrefix) {
return createReducer(List(), {
[actionTypePrefix + PUSH]: (state, action) => state.push(action.payload),
[actionTypePrefix + POP]: (state) => state.pop(),
})
}

const reducer = combineReducers({
a: listReducer('a.'),
b: listReducer('b.'),
c: listReducer('c.'),
d: combineReducers({
e: listReducer('d.e.'),
})
});

const aListActions = listActions('a.')
const bListActions = listActions('b.')
const deListActions = listActions('d.e.')

dispatch(aListActions.push(...))
dispatch(bListActions.pop())
dispatch(deListActions.push(...))
// etc
```

Example with `prefixReducer` and `prefixActionCreator` (which I only really like to do if I'm using 3rd-party reducers/action creators):

```js
import {
createReducer, combineReducers, prefixReducer, prefixActionCreator
} from 'mindfront-redux-utils'
import {mapValues} from 'lodash'
import {List} from 'immutable'

const PUSH = 'PUSH'
const POP = 'POP'

const push = (item) => ({type: pushType, payload: item})
const pop = () => ({type: popType})
const listActions = {push, pop}

const listReducer = createReducer(List(),
[PUSH]: (state, action) => state.push(action.payload),
[POP]: (state) => state.pop(),
})

const reducer = combineReducers({
a: prefixReducer('a.')(listReducer),
b: prefixReducer('b.')(listReducer),
c: prefixReducer('c.')(listReducer),
d: combineReducers({
e: prefixReducer('d.e.')(listReducer),
})
});

const aListActions = mapValues(listActions, prefixActionCreator('a.'))
const bListActions = mapValues(listActions, prefixActionCreator('b.'))
const deListActions = mapValues(listActions, prefixActionCreator('d.e.'))

dispatch(aListActions.push(...))
dispatch(bListActions.pop())
dispatch(deListActions.push(...))
// etc
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.