Skip to content

Latest commit

 

History

History
367 lines (302 loc) · 10.3 KB

File metadata and controls

367 lines (302 loc) · 10.3 KB

Frequently Asked Questions


How to navigate with Redux action

with store.dispatch

import { push } from 'connected-react-router'

store.dispatch(push('/path/to/somewhere'))

with react-redux

import { push } from 'connected-react-router'

// in component render:
<div onClick={() => {

  /** do something before redirection */
  props.push('/home');

}}>login</div>

// connect the action:
export default connect(null, { push })(Component);

in redux thunk

import { push } from 'connected-react-router'

export const login = (username, password) => (dispatch) => {

  /* do something before redirection */

  dispatch(push('/home'))
}

in redux saga

import { push } from 'connected-react-router'
import { put, call } from 'redux-saga/effects'

export function* login(username, password) {

  /* do something before redirection */

  yield put(push('/home'))
}

How to get the current browser location (URL)

The current browser location can be accessed directly from the router state with react-redux's connect. The location object is comprised of pathname, search (query string), and hash.

import { connect } from 'react-redux'

const Child = ({ pathname, search, hash }) => (
  <div>
    Child receives
    <div>
      pathname: {pathname}
    </div>
    <div>
      search: {search}
    </div>
    <div>
      hash: {hash}
    </div>
  </div>
)

const mapStateToProps = state => ({
  pathname: state.router.location.pathname,
  search: state.router.location.search,
  hash: state.router.location.hash,
})

export default connect(mapStateToProps)(Child)

How to set Router props (e.g. basename, initialEntries, etc.)

You can pass props to the create*History functions of your choice (createBrowserHistory, createHashHistory, createMemoryHistory)

import { createBrowserHistory } from 'history'

const history = createBrowserHistory({
  basename: '/prefix/',
})
import { createHashHistory } from 'history'

const history = createHashHistory({
  hashType: 'slash',
  getUserConfirmation: (message, callback) => callback(window.confirm(message))
})
import { createMemoryHistory } from 'history'

const history = createMemoryHistory({
  initialEntries: [ '/one', '/two', { pathname: '/three' } ],
  initialIndex: 1
})

How to hot reload functional components

  1. Save the main app component in its own file.

App.js

import React from 'react'
import { Route, Switch } from 'react-router' /* react-router v4/v5 */
import { ConnectedRouter } from 'connected-react-router'

const App = ({ history }) => ( /* receive history object via props */
  <ConnectedRouter history={history}>
    <div>
      <Switch>
        <Route exact path="/" render={() => (<div>Match</div>)} />
        <Route render={() => (<div>Miss</div>)} />
      </Switch>
    </div>
  </ConnectedRouter>
)

export default App
  1. Wrap the App component with AppContainer from react-hot-loader v3 as a top-level container.

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { AppContainer } from 'react-hot-loader' /* react-hot-loader v3 */
import App from './App'
...
const render = () => { // this function will be reused
  ReactDOM.render(
    <AppContainer> { /* AppContainer for hot reloading v3 */ }
      <Provider store={store}>
        <App history={history} /> { /* pass history object as props */ }
      </Provider>
    </AppContainer>,
    document.getElementById('react-root')
  )
}

render()
  1. Detect change and re-render with hot reload.

index.js

...
if (module.hot) {
  module.hot.accept('./App', () => {
    /* For Webpack 2.x
       Need to disable babel ES2015 modules transformation in .babelrc
       presets: [
         ["es2015", { "modules": false }]
       ]
    */
    render()

    /* For Webpack 1.x
    const NextApp = require('./App').default
    renderWithHotReload(NextApp)
    */
  })
}

Now, when you change any component that App depends on, it will trigger hot reloading without losing redux state. Thanks react-hot-loader v3!

How to hot reload reducers

Detect change and replace with a new root reducer with router state

index.js

...
if (module.hot) {
  module.hot.accept('./reducers', () => {
    /* For Webpack 2.x
       Need to disable babel ES2015 modules transformation in .babelrc
       presets: [
         ["es2015", { "modules": false }]
       ]
    */
    store.replaceReducer(rootReducer(history))

    /* For Webpack 1.x
    const nextRootReducer = require('./reducers').default
    store.replaceReducer(nextRootReducer(history))
    */
  })
}

How to support Immutable.js

  1. Create your root reducer as a function that takes history and returns reducer. Use combineReducers from redux-immutable to return the root reducer.

  2. Import connectRouter from connected-react-router/immutable and add router reducer to root reducer

import { combineReducers } from 'redux-immutable'
import { connectRouter } from 'connected-react-router/immutable'
...
const rootReducer = (history) => combineReducers({
  router: connectRouter(history),
  ...
})
...
  1. Import ConnectedRouter and routerMiddleware from connected-react-router/immutable instead of connected-react-router.
import { ConnectedRouter, routerMiddleware } from 'connected-react-router/immutable'
  1. Create your root reducer with router reducer by passing history to rootReducer function
const store = createStore(
  rootReducer(history),
  initialState,
  ...
)
  1. (Optional) Initialize state with Immutable.Map()
import Immutable from 'immutable'
...
const initialState = Immutable.Map()
...
const store = createStore(
  rootReducer(history),
  initialState,
  ...
)

How to migrate from v4 to v5/v6

It's easy to migrate from v4 to v5/v6.

  1. In your root reducer file, instead of exporting a root reducer, you need to export a function accepting a history object and returning a root reducer with router key. The value of the router key is connectedRouter(history).
  // reducers.js

  import { combineReducers } from 'redux'
+ import { connectRouter } from 'connected-react-router'

- export default combineReducers({
+ export default (history) => combineReducers({
+   router: connectRouter(history),
    ...
  })
  1. In createStore function, change to use the new function creating a root reducer.
  // configureStore.js
  ...
  import { createBrowserHistory } from 'history'
  import { applyMiddleware, compose, createStore } from 'redux'
- import { connectRouter, routerMiddleware } from 'connected-react-router'
+ import { routerMiddleware } from 'connected-react-router'
- import rootReducer from './reducers'
+ import createRootReducer from './reducers'

  const history = createBrowserHistory()

  const store = createStore(
-   connectRouter(history)(rootReducer),
+   createRootReducer(history),
    initialState,
    compose(
      applyMiddleware(
        routerMiddleware(history),
      ),
    ),
  )
  1. For reducers hot reloading, similarly, change to use the new function creating a root reducer.
  // For Webpack 2.x
- store.replaceReducer(connectRouter(history)(rootReducer))
+ store.replaceReducer(createRootReducer(history))

  // For Webpack 1.x
- const nextRootReducer = require('./reducers').default
- store.replaceReducer(connectRouter(history)(nextRootReducer))
+ const nextCreateRootReducer = require('./reducers').default
+ store.replaceReducer(nextCreateRootReducer(history))

How to use connected-react-router with react native

History does not exist, how can I configure my redux store?

As you know react native does not support natively the HTML5 history API, it's supposed to be available only for web browsers. This issue can be solved by using createMemoryHistory.

Here is an example with react-redux v6.0.0.

const history = createMemoryHistory()

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Route path="/" component={myComponent} exact={true} />
    </ConnectedRouter>
  </Provider>
)

Example available here

Get location from a screen

You can access at your location interface with history.location.

Example available here

Go to a screen with parameter

You can use history and navigate between screens.

Example available here

How to Use Your Own Context with react-redux

With react-redux v6.0.0, you can pass your own context to <Provider> component. So, you need to pass the same context as props to <ConnectedRouter> component.

const customContext = React.createContext(null) // your own context

ReactDOM.render(
  <Provider store={store} context={customContext}>
    <ConnectedRouter history={history} context={customContext}>
      ...
    </ConnectedRouter>
  </Provider>
)

How to stop initial location change

In order to make this package more compatible with react-router-redux, a LOCATION_CHANGE action is dispatched for the initial location. This can however be disabled via the noInitialPop prop.

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history} noInitialPop>
      <Route path="/" component={myComponent} exact={true} />
    </ConnectedRouter>
  </Provider>
)