Last active
November 9, 2019 01:06
-
-
Save mswanson/dbef36a1115fa97cc810fd2aedc1e0a5 to your computer and use it in GitHub Desktop.
Wiring up Redux-ORM with Redux Saga and Slice Reducers
This file contains hidden or 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
// called in app root js file | |
// Classes and functions we need to build the store and initial state | |
import { createStore, applyMiddleware } from 'redux'; | |
import createSagaMiddleware from 'redux-saga'; | |
import InitialStateBuider from './initial-state-builder'; | |
// Sagas and Reducers | |
import rootSaga from '../modules/root-saga'; | |
import rootReducer from '../modules/root-reducer'; | |
// Debugging Tools | |
// ReduxDevTools: https://github.com/zalmoxisus/redux-devtools-extension | |
// Redux Logger: https://github.com/evgenyrodionov/redux-logger | |
import { composeWithDevTools } from 'redux-devtools-extension'; | |
import logger/*, { createLogger } */ from 'redux-logger'; | |
// const logger = createLogger({ /* add custom options to the logger */ }); | |
// Connect the sagas to Redux | |
export const sagaMiddleware = createSagaMiddleware(); | |
// Configure, create and export the store | |
export default function configureStore(props) { | |
let middlewares = [sagaMiddleware]; | |
// Include Redux Logger for development | |
if (process.env.NODE_ENV === 'development') { middlewares = [...middlewares, logger]; } | |
// build the initial state from the passed in props | |
const initialState = new InitialState(); | |
const state = initialStateBuilder.buildFromProps(props); | |
// return the configured store | |
const store = createStore(rootReducer, state, composeWithDevTools(applyMiddleware(...middlewares))); | |
// Initialize the sagas | |
sagaMiddleware.run(rootSaga); | |
return store; | |
} |
This file contains hidden or 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 * as ACTIONS from './actions'; | |
/** | |
* This function gets called from the entitiesReducer in `../root-reducer.js` | |
* and delegates actions to functions within this file based on the action type | |
* @param {Object} state the entities Redux state slice | |
* @param {Object} action the called action | |
* @param {Object} session the Redux-ORM session for the current state | |
*/ | |
export function handleCourseActions(state, action, session) { | |
switch (action.type) { | |
case ACTIONS.SAVING_CHANGES_SUCCESS: | |
case ACTIONS.SAVING_CHANGES_FAILURE: | |
saveChangesReducer(state, action, session); | |
break; | |
case ACTIONS.DISCARDING_CHANGES_SUCCESS: | |
case ACTIONS.DISCARDING_CHANGES_FAILURE: | |
discardChangesReducer(state, action, session); | |
break; | |
case ACTIONS.CANCELING_WIZARD_SUCCESS: | |
case ACTIONS.CANCELING_WIZARD_FAILURE: | |
cancelWizardReducer(state, action, session); | |
break; | |
case ACTIONS.ACTIVATE_OUTCOME_LIST_MODE: | |
activateOutcomeListModeReducer(state, action, session); | |
break; | |
} | |
} | |
function saveChangesReducer(state, action, session) { | |
// do stuff here... | |
} | |
function discardChangesReducer(state, action, session) { | |
// do stuff here... | |
} | |
function cancelWizardReducer(state, action, session) { | |
// do stuff here... | |
} | |
function activateOutcomeListModeReducer(state, action, session) { | |
const { Courses } = session; | |
const { listMode, courseGuid } = action.payload; | |
Courses.withId(courseGuid).set('outcomeListMode', listMode); | |
} |
This file contains hidden or 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
// this class gets called from configure-store.js | |
export default class InitialStateBuilder { | |
constructor() { | |
this.entities = schema.getEmptyState(); | |
this.session = schema.mutableSession(this.entities); | |
} | |
//... other code | |
buildFromProps(props) { | |
const { course: courseState } = props; | |
const { Courses, Outcomes } = this.session; | |
// ...build initial state from props | |
return { | |
entities: {...this.entities}, | |
metadata | |
}; |
This file contains hidden or 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 schema from './schema'; | |
import { combineReducers } from 'redux'; | |
// Actions | |
import * as COURSE_ACTIONS from './courses/actions'; | |
import * as DWA_ACTIONS from './dwas/actions'; | |
import * as OUTCOME_ACTIONS from './outcomes/actions'; | |
import * as WORK_ACTIVITY_ACTIONS from './work-activities/actions'; | |
// Reducer functions | |
import { handleCourseActions } from './courses/reducers'; | |
import { handleDwaActions } from './dwas/reducers'; | |
import { handleOutcomeActions } from './outcomes/reducers'; | |
import { handleWorkActivityActions } from './work-activities/reducers'; | |
/* NOTE: Becuase Redux-ORM requires the entire entities slice of the store to be | |
* passed to it, we cannot break this reducer up into seperate reducer files. As | |
* a work around we created reducer functions in each module that take three params: | |
* | |
* state: the combined entities state slice | |
* action: the action | |
* session: the Redux-ORM session | |
* | |
* This top level reducer creates the session and hands off the actual work of updating | |
* the session state to the reducer functions. | |
* | |
* The last line of this reducer `return session.state` returns a new state based on the | |
* changes made in the reducer functions to the session.state. | |
**/ | |
function entitiesReducer(state = schema.getEmptyState(), action) { | |
let session; | |
// pass the state, action and session to the actual reducers | |
switch (true) { | |
// COURSES | |
case Object.values(COURSE_ACTIONS).includes(action.type): | |
session = schema.session(state); | |
handleCourseActions(state, action, session); | |
break; | |
// OUTCOMES | |
case Object.values(OUTCOME_ACTIONS).includes(action.type): | |
session = schema.session(state); | |
handleOutcomeActions(state, action, session); | |
break; | |
// WORK_ACTIVITIES | |
case Object.values(WORK_ACTIVITY_ACTIONS).includes(action.type): | |
session = schema.session(state); | |
handleWorkActivityActions(state, action, session); | |
break; | |
// DWAS | |
case Object.values(DWA_ACTIONS).includes(action.type): | |
session = schema.session(state); | |
handleDwaActions(state, action, session); | |
break; | |
} | |
// Finally, return state either the default or the updated session.state | |
return session ? session.state : state; | |
} | |
// export the conbined reducer | |
export default combineReducers({ | |
metadata: metadataReducer, | |
entities: entitiesReducer, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment