diff --git a/src/index.js b/src/index.js
index e3fb93733..c2a7231dd 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,23 +1,11 @@
import React from 'react'
-import { DndProvider } from 'react-dnd'
-import { HTML5Backend } from 'react-dnd-html5-backend'
import ReactDOM from 'react-dom'
-import { Provider } from 'react-redux'
import { rootDomId } from 'client/util'
+import App from './router'
import './client/websockets'
-import { LayoutFlagsProvider } from 'contexts/LayoutFlagsContext'
-import { clientRouter, history } from './router'
-import createStore from './store'
-
-const store = createStore(history)
+import './css/global/index.scss'
ReactDOM.render(
-
-
-
- {clientRouter()}
-
-
- ,
+ ,
document.getElementById(rootDomId)
)
diff --git a/src/router/index.js b/src/router/index.js
index c637b1bf0..8d6aff4cd 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,38 +1,31 @@
import React from 'react'
-import { StaticRouter, Switch, Route } from 'react-router'
import { ConnectedRouter } from 'connected-react-router'
-import { createBrowserHistory, createMemoryHistory } from 'history'
-import HyloAppRouter from 'routes/HyloAppRouter'
+import { Switch, Route } from 'react-router'
+import { DndProvider } from 'react-dnd'
+import { HTML5Backend } from 'react-dnd-html5-backend'
+import { Provider } from 'react-redux'
+import { LayoutFlagsProvider } from 'contexts/LayoutFlagsContext'
+import createStore, { history } from '../store'
import RootRouter from 'routes/RootRouter'
-import '../css/global/index.scss'
+import HyloAppRouter from 'routes/HyloAppRouter'
-export const history = typeof window !== 'undefined'
- ? createBrowserHistory()
- : createMemoryHistory()
+const store = createStore()
-// Note: `/hyloApp/*` routes will not invoke auth as auth cookie is already passed from webview
-export function clientRouter () {
+export default function App () {
require('client/rollbar') // set up handling of uncaught errors
return (
-
-
-
-
-
-
- )
-}
-
-// Note: Server-side Rendering
-// ref: https://github.com/Hylozoic/hylo-evo/issues/1069
-export function serverRouter (req, context) {
- return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/src/server/appMiddleware.js b/src/server/appMiddleware.js
index 118f2fb2e..725599f97 100644
--- a/src/server/appMiddleware.js
+++ b/src/server/appMiddleware.js
@@ -1,33 +1,54 @@
import { getBrowserSnippet } from './newrelic' // this must be first
import React from 'react'
-import { once } from 'lodash'
-import root from 'root-path'
import { renderToString } from 'react-dom/server'
-import { readFileSync } from 'fs'
import { Provider } from 'react-redux'
import { createMemoryHistory } from 'history'
-import { serverRouter } from 'router'
+import { StaticRouter } from 'react-router'
+import root from 'root-path'
+import { readFileSync } from 'fs'
+import { once } from 'lodash'
import createStore from '../store'
+import RootRouter from 'routes/RootRouter'
+
+/*
+Server-side Rendering Configuration
+
+Doesn't do much for us currently, ref:
+https://github.com/Hylozoic/hylo-evo/issues/1069
+
+* To remove simply eliminate the `renderToString` setup below,
+and change the last line of this function to be:
+
+`return res.status(200).send(html(''))`
+
+* The NewRelic setup could be revisited.
+
+*/
export default function appMiddleware (req, res, next) {
- // Note: add async data loading for more effective SSR
- const history = createMemoryHistory()
- const store = createStore(history)
+ // Note: Add async data loading here for more effective SSR
+ const store = createStore(createMemoryHistory())
const context = {}
const markup = renderToString(
- {serverRouter(req, context)}
+
+
+
)
- // context may now have been mutated; check its values and redirect,
- // show an error, etc. as appropriate
- // https://reacttraining.com/react-router/web/guides/server-rendering
+ /*
+
+ Context may now have been mutated; check its values and redirect,
+ show an error, etc. as appropriate, ref:
+ https://v5.reactrouter.com/web/guides/server-rendering
+
+ */
return res.status(200).send(html(markup))
}
-// this is set up as a property to make it easy to mock in tests
+// A property to make it easy to mock in tests
appMiddleware.getIndexFile = once(() => {
const indexPath = root('build/index.html')
return readFileSync(indexPath, { encoding: 'utf-8' })
diff --git a/src/store/index.js b/src/store/index.js
index 103c347d3..702213b3d 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,11 +1,22 @@
import { createStore } from 'redux'
import createMiddleware from './middleware'
-import rootReducer, { combinedReducers } from './reducers'
+import { createBrowserHistory } from 'history'
+import createRootReducer, { createCombinedReducers } from './reducers'
+
+// `window` check here is for SSR case (see `appMiddleware`)
+// where a static memory history is passed
+export const history = typeof window !== 'undefined' && createBrowserHistory()
+
+export function getEmptyState (providedHistory = history) {
+ const combinedReducers = createCombinedReducers(providedHistory)
-export function getEmptyState () {
return combinedReducers({}, { type: '' })
}
-export default function (history, req) {
- return createStore(rootReducer, getEmptyState(), createMiddleware(history, req))
+export default function configureStore (providedHistory = history, req) {
+ return createStore(
+ createRootReducer(providedHistory),
+ getEmptyState(providedHistory),
+ createMiddleware(providedHistory, req)
+ )
}
diff --git a/src/store/middleware/optimisticMiddleware.js b/src/store/middleware/optimisticMiddleware.js
index 431d719be..dabb8c169 100644
--- a/src/store/middleware/optimisticMiddleware.js
+++ b/src/store/middleware/optimisticMiddleware.js
@@ -4,7 +4,8 @@ import { SET_STATE } from 'store/constants'
export default function optimisticMiddleware (store) {
return next => action => {
- let { payload, meta } = action
+ const { payload, meta } = action
+
if (get('optimistic', meta) && isPromise(payload)) {
const prevState = store.getState()
action.payload = action.payload.then(
@@ -15,6 +16,7 @@ export default function optimisticMiddleware (store) {
}
)
}
+
return next(action)
}
}
diff --git a/src/store/reducers/index.js b/src/store/reducers/index.js
index fd7eb6dd1..d72815fbb 100644
--- a/src/store/reducers/index.js
+++ b/src/store/reducers/index.js
@@ -1,6 +1,5 @@
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'
-import { history } from 'router'
import orm from './ormReducer'
import returnToPath from 'store/reducers/returnToPath'
@@ -37,7 +36,7 @@ import SkillsToLearnSection from 'components/SkillsToLearnSection/SkillsToLearnS
import TopicsSettings from 'routes/GroupSettings/TopicsSettingsTab/TopicsSettingsTab.store'
import UserGroupsTab from 'routes/UserSettings/UserGroupsTab/UserGroupsTab.store'
-export const combinedReducers = combineReducers({
+export const createCombinedReducers = history => combineReducers({
// Global store
orm,
router: connectRouter(history),
@@ -74,8 +73,21 @@ export const combinedReducers = combineReducers({
UserGroupsTab
})
-export default composeReducers(
- combinedReducers,
- resetStore,
- handleSetState
-)
+export default function createRootReducer (history) {
+ return composeReducers(
+ createCombinedReducers(history),
+ /*
+
+ DANGEROUS: These mutate and/or reset the entire state object
+
+ Not sure why then need to added using our `composeReducers`
+ utility function with appears to do the same things as redux's
+ `combineReducers`. If I remember right correctly this is so these
+ somehow run in a 2nd reducer cycle to eliminate an infinite reducer
+ update condition? Not convinced they can't just go at the bottom above ^
+
+ */
+ resetStore,
+ handleSetState
+ )
+}
diff --git a/src/store/reducers/resetStore.js b/src/store/reducers/resetStore.js
index b8b7bc8fd..6538a419f 100644
--- a/src/store/reducers/resetStore.js
+++ b/src/store/reducers/resetStore.js
@@ -8,7 +8,7 @@ export const KEYS_PRESERVED_ON_RESET = [
'mixpanel'
]
-export default function (state, action) {
+export default function (state = null, action) {
if (action.type === LOGOUT && !action.error) {
return getEmptyState()
}
diff --git a/src/util/testing/reactTestingLibraryExtended.js b/src/util/testing/reactTestingLibraryExtended.js
index 2c89cf842..9f7e41014 100644
--- a/src/util/testing/reactTestingLibraryExtended.js
+++ b/src/util/testing/reactTestingLibraryExtended.js
@@ -5,20 +5,19 @@ import { MemoryRouter } from 'react-router'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import { render } from '@testing-library/react'
-import { history } from 'router'
-import rootReducer from 'store/reducers'
+import createRootReducer from 'store/reducers'
import createMiddleware from 'store/middleware'
-import { getEmptyState } from 'store'
+import { history, getEmptyState } from 'store'
import { LayoutFlagsProvider } from 'contexts/LayoutFlagsContext'
// Note: This is ran by default via `customRender` below, but it's necessary to manually
// generate the store when pre-populating the ReduxORM in a test. Search across tests to
// for examples. Merges `provideState` over default app empty state
-export function generateStore (providedState, providedHistory) {
+export function generateStore (providedState, providedHistory = history) {
return createStore(
- rootReducer,
+ createRootReducer(providedHistory),
{ ...getEmptyState(), ...providedState },
- createMiddleware(providedHistory || history)
+ createMiddleware(providedHistory)
)
}