Skip to content

Instantly share code, notes, and snippets.

@mswanson
Last active November 9, 2019 01:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mswanson/dbef36a1115fa97cc810fd2aedc1e0a5 to your computer and use it in GitHub Desktop.
Save mswanson/dbef36a1115fa97cc810fd2aedc1e0a5 to your computer and use it in GitHub Desktop.
Wiring up Redux-ORM with Redux Saga and Slice Reducers
// 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;
}
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 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
};
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