How To Use React-Router-Dom V4 In React-Redux-Router

Ahamed Fazil Buhari
 
Senior Developer
December 1, 2018
 
Rate this article
 
Views
2957

Hey,

If we use redux for state management in your react application, then react-redux-router plays vital role when we want routing in our application. It looks bit complex to configure react-redux-router but believe me routing will be very easy once it’s configured. Version is pretty important here for react-router-redux and here I am using 5.0.0-alpha.9 (maybe by the time you read this article, you will have better version)

 

 

I am using typescript so I need to add install @types in devDependencis

Once we have this packages installed, we need to configure in three places –

1. Reducer Function

2. Store Creation

3. Assign history to ConnectedRouter in parent tag

4. Push (whenever necessary)

1. Reducer Function

I have combineReducers in my app and all I need to do is, refer the routerReducer from react-router-redux package

RootReducer.ts

 import { combineReducers } from "redux";
 import { routerReducer } from "react-router-redux";
 import IStore from "../store/IStore";
 import { userReducer } from "./UserReducer";
 import { appReducer } from "./AppReducer";
 
 const rootReducer = combineReducers<IStore>({
   routing: routerReducer,
   user: userReducer,
   app: appReducer
 });
 
 export default rootReducer;
 

export default rootReducer;

2. Store Creation

In store creation, I’ve created history from history package, I will be using hash navigation so I specifically take history from createHashHistory and add this to my story using routerMiddleware

configureStore.dev

 import { applyMiddleware, createStore, Store } from "redux";
 import promise from "redux-promise-middleware";
 import thunkMiddleware from "redux-thunk";
 import { routerMiddleware } from "react-router-redux";
 import createHistory from "history/createHashHistory";
 // For Dev
 import logger from "redux-logger";
 import { composeWithDevTools } from "redux-devtools-extension";
 import rootReducer from "../reducers/RootReducer";
 import IStore from "./IStore";
 import initialState from "./initialState";
 
 export const history = createHistory();
 
 export default function configureStore(
   initialStateValue: IStore = initialState
 ): Store<IStore> {
   return createStore(
     rootReducer,
     initialStateValue!,
     composeWithDevTools(
       applyMiddleware(
         promise(),
         routerMiddleware(history),
         thunkMiddleware,
         logger
       )
     )
   );
 }
 

3. Assign history to ConnectedRouter in parent tag

In our root tag, just below the <Provider> tag which we used for redux, add <ConnectedRouter> and assign all the components inside this tag, make sure to use history which we created in store.

index.tsx

 import * as React from "react";
 import * as ReactDOM from "react-dom";
 import { Provider } from "react-redux";
 import { Fabric } from "office-ui-fabric-react";
 import App from "./containers/App";
 import "./styles/app.scss";
 import configureStore from "./store/configureStore";
 import { ConnectedRouter } from "react-router-redux";
 import { history } from "../src/store/configureStore.dev";
 
 const storeObj = configureStore();
 
 ReactDOM.render(
   <Fabric>
     <Provider store={storeObj}>
       <ConnectedRouter history={history}>
         <App />
       </ConnectedRouter>
     </Provider>
   </Fabric>,
   document.getElementById("root") as HTMLElement
 );
 

I created a component called AppNavigation and it will take care of navigation between components and we are using CommandBar from Office UI Fabric for navigation bar. Important thing to notice here is this.props.onChangePath(item.key) this will push the values into router during onClick event

AppNavigation.tsx

 import * as React from "react";
 import { Dispatch } from "react-redux";
 import { RouterState } from "react-router-redux";
 import { CommandBar } from "office-ui-fabric-react";
 
 export interface IAppNavigationProps {
   onChangePath: (key: string) => Dispatch<RouterState>;
 }
 
 export default class AppNavigation extends React.Component<IAppNavigationProps, {}> {
   constructor(props: IAppNavigationProps) {
     super(props);
   }
 
   public render(): JSX.Element {
     let isAdmin = true;
     const menuItems = [
       {
         key: "home",
         name: "Home",
         iconProps: {
           iconName: "Home"
         },
         onClick: (event: any, item: any) => {
           this.props.onChangePath(item.key);
         }
       },
       {
         name: "Add New",
         iconProps: {
           iconName: "Add"
         },
         key: "form",
         subMenuProps: {
           items: [
             {
               name: "Page 1",
               key: "page1",
               iconProps: {
                 iconName: "NewTeamProject"
               },
               className: isAdmin ? "" : "hidden",
               onClick: (event: any, item: any) => {
                 this.props.onChangePath(item.key);
               }
             },
             {
               name: "Page 2",
               key: "/page2",
               iconProps: {
                 iconName: "ProjectCollection"
               },
               onClick: (event: any, item: any) => {
                 this.props.onChangePath(item.key);
               }
             }
           ]
         }
       }
     ];
     return (
       <nav>
         <CommandBar items={menuItems} />
       </nav>
     );
   }
 }
 
 

 

Actual export for AppNavigation will contain this dispatch(push(key)); in this push is from react-router-redux

 import IStore from "../store/IStore";
 import { Dispatch } from "redux";
 import { connect } from "react-redux";
 import AppNavigation, { IAppNavigationProps } from "../components/AppNavigation";
 import { push } from "react-router-redux";
 
 function mapStateToProps(store: IStore) {
   return {
     store: store
   };
 }
 
 function mapDispatchToProps(dispatch: Dispatch<IStore>) {
   return {
     onChangePath: (key: string) => {
       dispatch(push(key));
     }
   };
 }
 
 export default connect<{}, {}, IAppNavigationProps>(
     mapStateToProps,
     mapDispatchToProps
   )(AppNavigation) as React.ComponentClass<{}>;
 
 
 

 

 

In our main App.tsx file, I use below element inside render function,

 <div>
             <div>
               <Route path="/" component={AppNavigation} />
 
               <div className="Grid">
                 <Route exact path="/" render={() => <Redirect to="/home" />} />
                 <Route path="/home" render={() => <h4>Home</h4>} />
                 <Route path="/page1" component={HelpIcon} />
                 <Route path="/page2" render={() => <h4>yet to develop</h4>} />
               </div>
             </div>
           </div>
 
 
 

Finally, we setup of hashRouter using react-router-redux package.

And in the logs we can see how store gets updated by router

Github repo for this solution is available here (branch name is: feature/redux-router) -> https://github.com/ahamedfazil/react-redux-ts-sp/tree/feature/redux-router

Happy Coding

Ahamed

Category : React

Author Info

Ahamed Fazil Buhari
 
Senior Developer
 
Rate this article
 
Ahamed is a Senior Developer and he has very good experience in the field of Microsoft Technologies, especially SharePoint, Azure, M365, SPFx, .NET and client side scripting - JavaScript, TypeScript, ...read more
 

Leave a comment