title: How to reuse action creators in react-redux?
body: <p>My question is about how to structure reducers and actions creators in order to reuse them correctly. 
  I've read tons of bibliography in the web about reducers composition and higher order reducers and managed to get some steps into the correct direction by creating a namespaced reducer factory/generator. With this i can have different instances of the same component/view with independent states, that share common behaviour. However this is not true for components/views that have the share some common grounds but are not equal. Lets say... the show and edit view for an entity. </p>
  
  <p>On mount, both of this components needs to fetch the entity data from an API in the same way, however the show component has much less features than the edit component which also processes form submit, handles errors, etc...</p>
  
  <p>So, having said that... how i'm suppose to extend the editEntityReducer and editEntity action creators to include the entityReducer and entity action creators plus the edits own reducer features and action creators?</p>
  
  <p>This is what i have so far, using a User entity as an example:</p>
  
  <h1>User reducer + action creators (user.js):</h1>
  
  <pre><code>import normalize from 'jsonapi-normalizer'
  import { api, authenticatedHeaders } from 'api'
  import { RSAA } from 'redux-api-middleware'
  import { List, Record } from 'immutable'
  import * as constants from './constants'
  
  // ------------------------------------
  // Actions
  // ------------------------------------
  export const destroyUser = (userId) =&gt; {
    // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
    return {
      [RSAA]: {
        endpoint: api.users.destroy.path(userId),
        method: api.users.destroy.method,
        headers: (state) =&gt; authenticatedHeaders(state.session.authorization.token),
        types: [
        constants.DESTROY_START,
        constants.DESTROY_SUCCESS,
        constants.DESTROY_FAIL]
      }
    }
  }
  
  export const fetchUser = (userId) =&gt; {
    // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
    return {
      [RSAA]: {
        endpoint: api.users.show.path(userId),
        method: api.users.show.method,
        headers: (state) =&gt; authenticatedHeaders(state.session.authorization.token),
        types: [
        constants.FETCH_START,
        {
          type: constants.FETCH_SUCCESS,
          payload: (action, state, res) =&gt; {
            return res.json().then(json =&gt; normalize(json))
          }
        },
        constants.FETCH_FAIL]
      }
    }
  }
  
  // ------------------------------------
  // Action Handlers
  // ------------------------------------
  const ACTION_HANDLERS = (prefix) =&gt; {
    return {
      [`${prefix}_${constants.DESTROY_START}`]: (state, action) =&gt; {
        return state.set('destroying', true)
      },
  
      [`${prefix}_${constants.DESTROY_SUCCESS}`]: (state, action) =&gt; {
        return state.set('destroying', false)
      },
  
      [`${prefix}_${constants.DESTROY_FAIL}`]: (state, action) =&gt; {
        return state.set('destroying', false)
      },
  
      [`${prefix}_${constants.FETCH_START}`]: (state, action) =&gt; {
        return state.set('loading', true)
      },
  
      [`${prefix}_${constants.FETCH_SUCCESS}`]: (state, { payload }) =&gt; {
        const users = payload.entities.user
        const userIds = payload.result.user
        const roles = payload.entities.role
  
        // It's a single record fetch
        const user = users[userIds[0]]
  
        return state.merge({
          loading: false,
          record: Record({ user: Record(user)(), roles: Record(roles)() })()
        })
      },
  
      [`${prefix}_${constants.FETCH_FAIL}`]: (state, action) =&gt; {
        return state.set('loading', false)
      }
    }
  
  }
  
  // ------------------------------------
  // Reducer
  // ------------------------------------
  const initialState = Record({
    destroying: false,
    loading: true, // initially true so will only go to false upong user loaded
    record: Record({ user: Record({})(), roles: List([]) })()
  })()
  
  const userReducer = (prefix = 'USER') =&gt; {
    if (prefix === undefined || prefix.length &lt; 1) {
      throw new Error('prefix must be defined')
    }
  
    return (state = initialState, action) =&gt; {
      const handler = ACTION_HANDLERS(prefix)[`${prefix}_${action.type}`]
      return handler ? handler(state, action) : state
    }
  }
  
  export default userReducer
  </code></pre>
  
  <h1>Edit user reducer and action creators (edit_user.js):</h1>
  
  <pre><code>import normalize from 'jsonapi-normalizer'
  import { api, authenticatedHeaders } from 'api'
  import { RSAA } from 'redux-api-middleware'
  import { List, Record } from 'immutable'
  import * as constants from './constants'
  
  // ------------------------------------
  // Actions
  // ------------------------------------
  export const updateUser = (userId, params = {}) =&gt; {
    // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
    return {
      [RSAA]: {
        endpoint: api.users.update.path(userId),
        method: api.users.update.method,
        headers: (state) =&gt; authenticatedHeaders(state.session.authorization.token),
        types: [
        constants.USER_UPDATE_START,
        constants.USER_UPDATE_SUCCESS,
        constants.USER_UPDATE_FAIL]
      }
    }
  }
  
  // TODO: see how to reuse this from the user.js file!
  export const fetchUser = (userId) =&gt; {
    // Uses redux-api-middleware. see: https://github.com/agraboso/redux-api-middleware
    return {
      [RSAA]: {
        endpoint: api.users.show.path(userId),
        method: api.users.show.method,
        headers: (state) =&gt; authenticatedHeaders(state.session.authorization.token),
        types: [
        constants.USER_FETCH_START,
        {
          type: constants.USER_FETCH_SUCCESS,
          payload: (action, state, res) =&gt; {
            return res.json().then(json =&gt; normalize(json))
          }
        },
        constants.USER_FETCH_FAIL]
      }
    }
  }
  
  // ------------------------------------
  // Action Handlers
  // ------------------------------------
  const ACTION_HANDLERS = {
    [constants.USER_UPDATE_START]: (state, action) =&gt; {
      return state.set('loading', true)
    },
  
    [constants.USER_UPDATE_SUCCESS]: (state, action) =&gt; {
      return state.set('loading', false)
    },
  
    [constants.USER_UPDATE_FAIL]: (state, action) =&gt; {
      return state.set('loading', false)
    },
  
    // TODO: this reducers are the same as user.js, reuse them!!
    [constants.USER_FETCH_START]: (state, action) =&gt; {
      return state.set('loading', true)
    },
  
    [constants.USER_FETCH_SUCCESS]: (state, { payload }) =&gt; {
      const users = payload.entities.user
      const userIds = payload.result.user
      const roles = payload.entities.role
  
      // It's a single record fetch
      const user = users[userIds[0]]
  
      return state.merge({
        loading: false,
        record: Record({ user: Record(user)(), roles: Record(roles)() })()
      })
    },
  
    [constants.USER_FETCH_FAIL]: (state, action) =&gt; {
      return state.set('loading', false)
    }
  
  }
  
  // ------------------------------------
  // Reducer
  // ------------------------------------
  const initialState = Record({
    loading: true, // initially true so will only go to false upong user loaded
    record: Record({ user: Record({})(), roles: List([]) })()
  })()
  
  export default function editUserReducer (state = initialState, action) {
    const handler = ACTION_HANDLERS[action.type]
  
    return handler ? handler(state, action) : state
  }
  </code></pre>
  
  <p>As you can see in the TODO's in the code, i want to be able to reuse those parts of reducer and action creator since it's not only reusable for an entity basic operations, but for any generic CRUD operation on any resource that my application may use!</p>
  
  <p>Thanks</p>