Skip to content

Redux Implementation Guide

Noranda Brown edited this page Apr 26, 2018 · 22 revisions

Redux Implementation Guide

Purpose

The purpose of this guide is to create a living document for conversion of Flux Notes from React to React+Redux.

Documents

Redux Training Slides

Redux Architecture Chart (as of 2017.12.14)

Questions/Inconsistencies to Consider:

  • What to do with all the data models and classes - should we convert them to prop types?
  • What to do with lib files - should they be separated or included in the app?

Tasks

The following tasks will need to be performed to successfully refactor Flux Notes to use Redux.

    • Install redux, and react-redux libraries
    • Reorganize and update directory structure for Redux
    • Set up routes, Root component, store, and root reducer
    • Refactor FullApp and SlimApp as Redux containers
    • Refactor remaining app files by recommended group below

Task Descriptions

1. Install redux, and react-redux libraries

$ yarn add --save redux react-redux

2. Reorganize and update directory structure for Redux

Since Redux is just a data store library, it has no direct opinion on how your project should be structured. However, most Redux developers tend to the following pattern.

  • Create the following folders under src/: actions, components, containers, reducers, styles, store, and utils
  • Move src/apps/LandingPage.jsx to src/components/LandingPage.js and update imports
  • Move src/apps/FullApp.jsx to src/containers/FullApp.js and update imports
  • Move src/apps/SlimApp.jsx to src/containers/SlimApp.js and update imports
src/
├── actions/
├── components/
|   ├── LandingPage.js
|   └── Root.js (create in step 3)
├── containers/
|   ├── FullApp.js
|   └── SlimApp.js
├── reducers/
|   └── index.js (create in step 3)
├── styles/
|   └── app.scss
├── store/
|   └── configureStore.js
├── utils/
|   └── tbd.js
├── routes.js (create in step 3)
└── index.js

3. Set up routes, Root component, store, and root reducer

  • Move src/analyticsTracker/WithTracker.jsx to src/components/WithTracker.js
  • Create src/routes.js file to handle routes (see example below)
  • Update src/index.js file to use new Root component (see example below)
  • Move any props from src/apps/AppManager.jsx into appropriate FullApp or SlimApp container and update imports
  • Update any remaining imports and verify routing works and all tests pass
  • Delete src/Apps.jsx and src/routes.js files
  • Add redux-promise-middleware (to handle middleware), redux-thunk (to handle async actions), and redux-logger (to handle logging) libraries
  • Implement src/store/configureStore.js (see example below - uses Redux DevTools)
  • Create src/reducers/index.js to set up root reducer (see example below)

Example routes.js:

import React from 'react';
import { Route } from 'react-router';

import WithTracker from './components/WithTracker';
import App from './components/App';
import LandingPage from './components/LandingPage';
import FullApp from './containers/FullApp';
import SlimApp from './containers/SlimApp';

export default (
  <Route component={App}>
    <Route exact path="/" component={WithTracker(LandingPage)} />
    <Route path="/patient" component={WithTracker(FullApp)} />
    <Route path="/patina" component={WithTracker(SlimApp)} />
  </Route>
);

Example srcs/index.js:

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';

import Root from './components/Root';
import configureStore from './store/configureStore';
import './styles/app.scss';

const store = configureStore();

window.store = store;

render(
  <Root store={store} />,
  document.getElementById('root')
);

Example src/store/configureStore.js:

import { createStore, applyMiddleware, compose } from 'redux';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from '../reducers';

export default function configureStore(initialState) {
  let middleware = applyMiddleware(
    promiseMiddleware(),
    thunkMiddleware,
    logger()
  );

  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
  let store = createStore(rootReducer, initialState, composeEnhancers(middleware));

  return store;
}

Example src/reducers/index.js:

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';

const rootReducer = combineReducers({
  routing: routerReducer
  // additional reducers added here
});

export default rootReducer;

4. Refactor FullApp and SlimApp as Redux containers

For each container:

  • Step 1 - Define mapStateToProps, and mapDispatchToProps and hook up to new redux store using connect
  • Step 2 - Move any corresponding stylesheets into /styles folder and update imports
  • Step 3 - Move any pieces of state that aren't component-specific to the redux store and pass in
  • Step 4 - Move any functions that aren't component-specific to the /utils folder and import
  • Step 5 - Refactor any data flow logic to be an action and pass in

5. Refactor remaining components by feature

Recommend refactoring features in groups of related components:

  • Group 1: LandingPage
  • Group 2: NavBar, FormList, ShortcutViewer, LandingPageForm
  • Group 3: DashboardViewManager
  • Group 4: PatientControlPanel, SummaryHeader
  • Group 5: PreEncounterView, PostEncounterView, EncounterView
  • Group 6: NotesPanel
  • Group 7: FluxNotesEditor, ContextPortal, ContextItem, EditorToolbar, StructuredFieldPlugin
  • Group 8: NoteAssistant, ContextTray, TemplateForm, ContextOptions
  • Group 9: TargetedDataPanel
  • Group 10: TargetedDataSubpanel
  • Group 11: TabularNameValuePairsVisualizer, NarrativeNameValuePairsVisualizer
  • Group 12: TimelineEventsVisualizer, TimelineLegend, HoverItem, Item
  • Group 13: TargetedDataControl, ConditionSelection, ClinicalEventSelection
  • Group 14: Minimap, SlateSuggestionsDist (lib)

For each group, perform the following steps to refactor to use Redux:

  • Step 1 - Move component into /components folder and update imports
  • Step 2 - Move any corresponding stylesheets into /styles folder and update imports
  • Step 3 - Move any corresponding utils in /utils folder and update imports
  • Step 4 - Move any pieces of state that aren't component-specific to the redux store and pass in
  • Step 5 - Move any functions that aren't component-specific to the /utils folder and import
  • Step 6 - Refactor any data flow logic to be an action and pass in

Recommend evaluating use of the following components which don't appear in the redux architecture:

  • ClinicalTrialForm (used in Shortcuts.json)
  • DataCaptureForm (not used)
  • DeceasedForm (used in Shortcuts.json)
  • FormSearch (not used)
  • ProgressionForm (used in Shortcuts.json)
  • StagingForm (used in Shortcuts.json)
  • ToxicityForm (used in Shortcuts.json)
  • DatePicker (used in ClinicalTrialForm, DeceasedForm, and ProgressionForm in this list)
Clone this wiki locally