Simpleton
Simpleton

Reputation: 6415

React redux is not changing routes

Updating this question to use connected-react-router instead of react-router-redux since it is not compatible with react-router v4.

I can't seem to get my routing working when dispatching an action. I suspect it's because I'm using sagas which aren't being configured properly.

I have a saga:

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

//...

yield call(push, '/dashboard')

The push function doesn't redirect the browser to the specified path despite the redux logs in webdev tools showing that the action was successfully dispatched.

The top level index.js file looks like:

import createSagaMiddleware from 'redux-saga'
import rootSaga from './redux/sagas'
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import logger from 'redux-logger'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import rootReducer from './redux/modules'
import { applyMiddleware, compose, createStore } from 'redux'
import { createBrowserHistory } from 'history'
import { routerMiddleware, connectRouter } from 'connected-react-router'

const history = createBrowserHistory()
const sagaMiddleware = createSagaMiddleware()
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
  connectRouter(history)(rootReducer),
  composeEnhancer(
    applyMiddleware(
      sagaMiddleware,
      routerMiddleware(history),
      logger
    )
  )
)

sagaMiddleware.run(rootSaga)

const render = () => {
  ReactDOM.render(
    <Provider store={store}>
      <App history={history} />
    </Provider>,
    document.getElementById('root')
  )
}

render()

registerServiceWorker()

The App.js file containing the root component has:

import { ConnectedRouter } from 'connected-react-router'
import { Route, Switch, Redirect } from 'react-router-dom'

const App = ({ history }) => {
  return (
    <ConnectedRouter history={history}>
      <Switch>
        { routes }
      </Switch>
    </ConnectedRouter>
  )
}

export default App

What's missing from this setup to make it work?

Dependency versions:

"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"connected-react-router": "^4.3.0"

Upvotes: 3

Views: 1830

Answers (4)

user1665355
user1665355

Reputation: 3393

What seems to work for me is to use withRoter (dont know if it is correct way or not):

export default withRouter(compose(withConnect)(App)); //wrapped in withRouter

Or

export default compose(
  withRouter,
  withConnect,
)(App);

And in redux-saga:

yield put(push('/newpage'));

I use react-boilerplate, https://github.com/react-boilerplate/react-boilerplate. I don't know if it is correct or not but this way the route changes in url and I get to the new route. If i don't use withRouter the route changes in url and nothing more happens... I found solution here https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/withRouter.md

Would like to have someone opinion on that. Folks at https://github.com/react-boilerplate/react-boilerplate maybe?

Upvotes: 0

yeshashah
yeshashah

Reputation: 1554

Sagas are implemented as Generator functions that yield objects to the redux-saga middleware

So your Saga should export a Generator function:

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

//...

export function* rootSaga() {
    return yield call(push, '/dashboard')
}

And rootSaga should be registered with the sagaMiddleware:

import { rootSaga } from './redux/sagas';
...
sagaMiddleware.run(rootSaga)
...

Reference: https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html

Upvotes: 0

amankkg
amankkg

Reputation: 5061

Unlike history's push method (which is an impure function), connected-react-router's push is an action creator and its result (action) must be dispatched to trigger a navigation.
To do so in redux-saga you have to use put, not call.

call creates a call effect.
When yielded, it simply executes given function with given arguments and returns a result. It is a good fit for (but not limited by) impure function calls (e.g. network request), by decoupling us from a direct execution of a function.

put creates a dispatch effect.
When yielded, it dispatches passed in action object. Thus, decoupling your code only from a direct call of dispatch, not action creator (which should be pure by design).

So, in your case, the solution would look like:

yield put(push('/dashboard'))

P.S: the same applies to react-router-redux's push

Upvotes: 3

Aprillion
Aprillion

Reputation: 22304

you need to wire up the router's middleware, e.g.:

import { browserHistory } from 'react-router'
import { routerMiddleware } from 'react-router-redux'

const sagaMw = createSagaMiddleware()
const routerMw = routerMiddleware(browserHistory)
const middleware = applyMiddleware(sagaMw, routerMw, logger)

Upvotes: 2

Related Questions