Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a base URL to an app using Redux-Router + React-Router

I'm using React-Router 1.0.0-rc3 together with Redux-Router 1.0.0-beta3.

When using React-Router, you can use useBasename with createHistory to set the base URL for an app, so that you can easily write an app that runs inside a subdirectory. Example:

Instead of this:

import { createHistory } from 'history';

let base = "/app_name/"

<Router history={browserHistory}>
  <Route path={base} component={App}></Route>
</Router>

<Link path={base + "some_path"}>some_path</Link>

You can write in this way using useBasename:

import { createHistory, useBasename } from 'history';

const browserHistory = useBasename(createHistory)({
  basename: "/app_name"
});

<Router history={browserHistory}>
  <Route path="/" component={App}></Route>
</Router>

<Link path="/some_path">some_path</Link>

However, in Redux-Router, you need to pass createHistory instead of history to a reducer:

const store = compose(
  applyMiddleware(m1, m2, m3),
  reduxReactRouter({
    routes,
    createHistory
  }),
  devTools()
)(createStore)(reducer);

How can we use useBasename in this case?

like image 797
chibicode Avatar asked Oct 27 '15 19:10

chibicode


People also ask

How do I add links to my React router?

To add the link in the menu, use the <NavLink /> component by react-router-dom . The NavLink component provides a declarative way to navigate around the application. It is similar to the Link component, except it can apply an active style to the link if it is active.

How do I Link React App with Redux?

The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.


3 Answers

For react-router v2 or v3 and using react-router-redux v4 instead of redux-router, the setup of the history object will look like this:

import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'

const browserHistory = useRouterHistory(createHistory)({
  basename: '<yourBaseUrl>'
})
const history = syncHistoryWithStore(browserHistory, store)

The rest of the setup is as usual when there is no extra base URL.

like image 90
Diego V Avatar answered Oct 17 '22 23:10

Diego V


You can create a function that wraps useBasename:

const createHistoryWithBasename = (historyOptions) => {
  return useBasename(createHistory)({
    basename: '/app_name',
    ...historyOptions
  })
}

And pass it to compose:

const store = compose(
  applyMiddleware(m1, m2, m3),
  reduxReactRouter({
    routes,
    createHistory: createHistoryWithBaseName
  }),
  devTools()
)(createStore)(reducer);
like image 27
chibicode Avatar answered Oct 17 '22 23:10

chibicode


The API changes very often so this is what worked for me (I'm using the 2.0.3 version of redux-simple-router). I have defined the custom history in a separate file:

import { useRouterHistory } from 'react-router'
import { createHistory, useBasename } from 'history'
import { baseUrl } from '../config'

const browserHistory = useRouterHistory(useBasename(createHistory))({
  basename: "/appgen"
});

Now I need to initialize the store:

import { syncHistory, routeReducer } from 'redux-simple-router'
import browserHistory from '../misc/browserHistory'

const rootReducer = combineReducers({
  // ...other reducers
  routing: routeReducer
});

const reduxRouterMiddleware = syncHistory(browserHistory);

const finalCreateStore = compose(
  // ...other middleware
  applyMiddleware(reduxRouterMiddleware),
)(createStore);


const store = finalCreateStore(rootReducer, initialState);

Eventually, you need to pass the history to the Router.

import browserHistory from './misc/browserHistory'
import routes from '../routes'

export default class Root extends Component {
  static propTypes = {
    history: PropTypes.object.isRequired
  };

  render() {
    return (
      <Router history={browserHistory}>
        {routes}
      </Router>
    )
  }
}

Before I came up with this, I was using a manual solution. I defined my own Link component and my own redux action that were responsible for prepending the base URL. It might be useful for someone.

Updated Link component:

import React, { Component } from 'react'
import { Link as RouterLink } from 'react-router'
import { baseUrl } from '../config'

export default class Link extends Component {
  render() {
    return <RouterLink {...this.props} to={baseUrl + '/' + this.props.to} />
  }
}

Custom action creator:

import { routeActions } from 'redux-simple-router'
import { baseUrl } from '../config'

export function goTo(path) {
  return routeActions.push(baseUrl + '/' + path);
}

Updated root route:

import { baseUrl } from './config'
export default (
  <Route component={App} path={baseUrl}>
    //...nested routes
  </Route>
);

Note that these custom tools only support pushing updated paths, not the location descriptor object.

like image 43
tobik Avatar answered Oct 17 '22 22:10

tobik