Search code examples
reduxreact-routerbrowser-historyhistory.jsreact-router-redux

Store browserHistory using history.js in react redux architecture with SSR


How can one persist the full router history of a user visiting an SSR react-redux app? I have tried modifying the react-redux-router package's reducer.js file as such...but when the user loads via SSR, the history array is reset.

/**
 * This action type will be dispatched when your history
* receives a location change.
   */
export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

 const initialState = {
    locationBeforeTransitions: null,
    locationHistory: []
}

/**
 * This reducer will update the state with the most recent location history 
 * has transitioned to. This may not be in sync with the router,     particularly
 * if you have asynchronously-loaded routes, so reading from and relying on
 * this state is discouraged.
 */
 export function routerReducer(state = initialState, { type, payload } = {})         {
 if (type === LOCATION_CHANGE) {

return { ...state,
  locationBeforeTransitions: payload,
  locationHistory: state.locationHistory.concat([payload]) }
 }

return state
}

ref: https://github.com/reactjs/react-router-redux/blob/master/src/reducer.js

However, I think this is supposed to be achieved in a middleware.

Irregardless, this (storing the entire previous session history) seems like a common enough use case that perhaps someone has already formulated a best practice.??

Perhaps even this full history is accessible via the historyjs object in react-router w/o react-router-redux.

I'm looking for answers to how to fulfill storing the full history of a user's session in the redux state and post it to my api server when the user closes the browser or navigates away from the site. (if this is not possible, i could just post it upon every navigation.) Then I would like to show this history in a 'recently viewed' list of pages on the users' home pages.


Solution

  • First of all, you don't have to meddle with the internals of react-redux-router.

    As you can see in the code you presented, react-redux-router exports a LOCATION_CHANGE action.

    You can use this action in a reducer of your own. Here's an example:

    // locationHistoryReducer.js
    import { LOCATION_CHANGE } from 'react-router-redux';
    
    export default function locationHistory(state = [], action) {
      if (action.type === LOCATION_CHANGE) {
        return state.concat([action.payload]);
      }
      return state;
    }
    

    However, this may be unnecessary. Your assumption that this can be be achieved with middleware is correct. Here's an example of a middleware layer:

    const historySaver = store => next => action => {
      if (action.type === LOCATION_CHANGE) {
        // Do whatever you wish with action.payload
        // Send it an HTTP request to the server, save it in a cookie, localStorage, etc.
      }
      return next(action)
    }
    

    And here's how to apply that layer in the store:

    let store = createStore(
      combineReducers(reducers),
      applyMiddleware(
        historySaver
      )
    )
    

    Now, how you save and load data is entirely up to you (and has nothing to do with react-router and the browser's history).

    In the official docs, they recommend injecting the initial state on the server side using a window.__PRELOADED_STATE__ variable.