9.8 KiB
Frequently Asked Questions
- How to navigate with Redux action
- How to get the current browser location (URL)
- How to set Router props e.g. basename, initialEntries, etc.
- How to hot reload functional components
- How to hot reload reducers
- How to support Immutable.js
- How to migrate from v4 to v5/v6
- How to use connected-react-router with react native
- How to use your own context with react-redux
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
- 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
- Wrap the
App
component withAppContainer
fromreact-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()
- 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
-
Create your root reducer as a function that takes
history
and returns reducer. UsecombineReducers
fromredux-immutable
to return the root reducer. -
Import
connectRouter
fromconnected-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),
...
})
...
- Import
ConnectedRouter
androuterMiddleware
fromconnected-react-router/immutable
instead ofconnected-react-router
.
import { ConnectedRouter, routerMiddleware } from 'connected-react-router/immutable'
- Create your root reducer with router reducer by passing
history
torootReducer
function
const store = createStore(
rootReducer(history),
initialState,
...
)
- (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.
- 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 withrouter
key. The value of therouter
key isconnectedRouter(history)
.
// reducers.js
import { combineReducers } from 'redux'
+ import { connectRouter } from 'connected-react-router'
- export default combineReducers({
+ export default (history) => combineReducers({
+ router: connectRouter(history),
...
})
- 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),
),
),
)
- 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>
)
Get location from a screen
You can access at your location interface with history.location
.
Go to a screen with parameter
You can use history
and navigate between screens.
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>
)