erikras / multireducer

A utility to wrap many copies of a single Redux reducer into a single key-based reducer.
MIT License
422 stars 23 forks source link

architecture comment #123

Open jedwards1211 opened 7 years ago

jedwards1211 commented 7 years ago

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:

Disadvantages:

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.

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):

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
jedwards1211 commented 7 years ago

In fact, the Redux docs now have an excellent guide on doing this here: https://github.com/reactjs/redux/blob/master/docs/recipes/reducers/ReusingReducerLogic.md

georgeawwad commented 7 years ago

I'm using the Redux guide for creating split reducers, but I'm not sure how to map these as props for a component. The guide above doesn't follow up on this. What's the next step?

jedwards1211 commented 7 years ago

@georgeawwad are you looking for react-redux?

georgeawwad commented 7 years ago

Right. I was only asking my question here because your docs link led me to a potential solution with my project. It's not specifically related to this repo.