React router redirect after action redux
Asked Answered
O

10

82

I'm using react-redux and react-router. I need to redirect after an action is dispatched.

For example: I have registration a few steps. And after action:

function registerStep1Success(object) {
    return {
        type: REGISTER_STEP1_SUCCESS,
        status: object.status
   };
}

I want to redirect to page with registrationStep2. How can I do this?

p.s. In history browser '/registrationStep2' has not been visited. This page appears only after successful result registrationStep1 page.

Oxeyed answered 29/2, 2016 at 18:28 Comment(1)
react-router-dom v4 solution can be found here: #42701629Kirwin
L
67

With React Router 2+, wherever you dispatch the action, you can call browserHistory.push() (or hashHistory.push() if that’s what you use):

import { browserHistory } from 'react-router'

// ...
this.props.dispatch(registerStep1Success())
browserHistory.push('/registrationStep2')

You can do this from async action creators too if that is what you use.

Lanie answered 1/3, 2016 at 12:51 Comment(11)
what are the benefits using redux-router in future, which now in beta?Oxeyed
If you ever want to have Redux DevTools replay routing transitions you’d need to choose between github.com/acdlite/redux-router and github.com/reactjs/react-router-redux. In this case I would recommend github.com/reactjs/react-router-redux because it is more stable and much simpler.Lanie
@DanAbramov how can we do a redirect after an action as ximet is asking? is that correct to do a "push" in the reducer that handles REGISTER_STEP1_SUCCESS?Lethargic
Is this solution still available? I don't seem to be able to get this working... The URL updates after I use browserHistory.push() but the view does not.Archaic
Nevermind, I got it working, I was using browserHistory.push() although my router used hashHistory. hashHistory.push() works like a charm.Archaic
@Archaic Im glad I found your comment above. Use hashHistory if the store is using it.Cascade
Does this still hold true for React Router 4+? Or is there now a better way to do this?Knopp
Tip: If you want to redirect (without pushing location to history) like <Redirect to="..." /> just use history.replace('/someroute').Declare
browserHistory no longer works, but there is a possibility to create your own history object and access it from anywhere.Brnaby
I know its for sake of example but you really shouldn't separate registration part 1 and 2 into 2 routes or 2 seperate forms unless you are doing it for the sake of email verification or some other purpose like that. Furthermore the browserHistory no longer works in new version of react, now they are suggesting to use the Redirect component which is extended from react-router-dom or the useHistory hook. I hate using hooks so i stick with Redirect and browserHistory when im working with older versions of react.Sinfonietta
For me even the import { browserHistory } from 'react-router' is not working. 'browserHistory' is not exported from 'react-router'Lazar
L
25

Have you checked out react-router-redux? This library makes it possible to sync react-router with redux.

Here is an example from the documentation of how you can implement the redirection with a push action from react-router-redux.

import { routerMiddleware, push } from 'react-router-redux'

// Apply the middleware to the store
const middleware = routerMiddleware(browserHistory)
const store = createStore(
  reducers,
  applyMiddleware(middleware)
)

// Dispatch from anywhere like normal.
store.dispatch(push('/foo'))
Lucrece answered 1/3, 2016 at 0:10 Comment(3)
I know about it routing. But, I wonder is it possible to use standard react-routingOxeyed
And how redirect on the other page after Action (using react-router-redux).?Oxeyed
react-router-redux in now deprecated. Have a look instead at github.com/supasate/connected-react-routerSlating
G
8

Simplest solution for router version 4+: We use "react-router-dom": "4.3.1" It doesn't work with version 5+

export your browser history from the place where it was initialised and use browserHistory.push('/pathToRedirect'):

Package history must be installed(example: "history": "4.7.2"):

npm install --save history

In my project I initialise browser history in index.js:

import { createBrowserHistory } from 'history';

export const browserHistory = createBrowserHistory();

Redirect in the action:

export const actionName = () => (dispatch) => {
    axios
            .post('URL', {body})
            .then(response => {
                // Process success code
                  dispatch(
                    {
                      type: ACTION_TYPE_NAME,
                      payload: payload
                    }
                  );
                }
            })
            .then(() => {
                browserHistory.push('/pathToRedirect')
            })
            .catch(err => {
                // Process error code
                    }
                );
            });
};
Guarantee answered 23/4, 2019 at 14:26 Comment(5)
It does change the url, but doesn't effects the 'react-router'. If you're using this module - I'll advice to look for a different solution.Blindfold
This solution still works perfectly, but there is a lot of other.Guarantee
Use "react-router-dom": "4+" for this solution. We use "react-router-dom": "4.3.1"Guarantee
I just tried that again, still won't work. I actually use "react-router-dom": "5.2.0", even though the url changes the browser won't surf to the required page. I think I might open a different question, this seems weird.Blindfold
This solution doesn't work with Router v5. Use "react-router-dom": "4+" for this solution. We use "react-router-dom": "4.3.1Guarantee
K
5

To build on Eni Arinde previous answer's (I don't have the reputation to comment), here is how to use the store.dispatch method after an async action :

export function myAction(data) {
    return (dispatch) => {
        dispatch({
            type: ACTION_TYPE,
            data,
        }).then((response) => {
            dispatch(push('/my_url'));
        });
    };
}

The trick is to do it in the action files and not in the reducers, since reducers should not have side effects.

Kleist answered 28/7, 2017 at 7:57 Comment(4)
Even that this solution works, actions shouldn't be aware of routing at all IMO. You should be able to dispatch an action without any routing.Quassia
Is this a right way to approach the problem? Should we pass history object from component to the action creator for routing?Youngyoungblood
An alternative - github.com/johanneslumpe/redux-history-transitionsEnciso
How could you chain dispatch calls? dispatch().then(()=>dispatch) ? It doesn't seem to work. 'then is not a function'Ingest
K
3

We can use "connected-react-router".

    import axios from "axios";
    import { push } from "connected-react-router";
    
    export myFunction = () => {
      return async (dispatch) => {
        try {
          dispatch({ type: "GET_DATA_REQUEST" });
          const { data } = await axios.get("URL");
          dispatch({
            type: "GET_DATA_SUCCESS",
            payload: data
          });
        } catch (error) {
          dispatch({
            type: "GET_DATA_FAIL",
            payload: error,
          });
          dispatch(push("/notfound"));
        }
      };
    };

Attention-- Please go to https://github.com/supasate/connected-react-router read the docs and set up the connected-react-router first, and then use "push" from connected-react-router.

Kania answered 4/2, 2021 at 4:3 Comment(0)
L
2

You can use {withRouter} from 'react-router-dom'

Example below demonstrates a dispatch to push

export const registerUser = (userData, history) => {
  return dispatch => {
    axios
    .post('/api/users/register', userData)
    .then(response => history.push('/login'))
    .catch(err => dispatch(getErrors(err.response.data)));
  }
}

The history arguments is assigned to in the component as the a second parameter to the action creator (in this case 'registerUser')

Loess answered 20/4, 2018 at 0:49 Comment(1)
Could you please share the relevant imports, and the rest of the code?Blindfold
B
2

An updated answer using hooks; for router v5 users.

Working on react-router-dom:5.1.2.

No installation of external package is required.

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

You can use the history as you're previously used to.

More more details and APIs - read the manual

Blindfold answered 23/6, 2020 at 11:2 Comment(0)
S
0
signup = e => {
  e.preventDefault();
  const { username, fullname, email, password } = e.target.elements,
    { dispatch, history } = this.props,
    payload = {
      username: username.value,
      //...<payload> details here
    };
  dispatch(userSignup(payload, history));
  // then in the actions use history.push('/<route>') after actions or promises resolved.
};

render() {
  return (
    <SignupForm onSubmit={this.signup} />
    //... more <jsx/>
  )
}
Shouse answered 12/7, 2018 at 10:16 Comment(0)
K
0

Here is the working copy of routing app

    import {history, config} from '../../utils'
        import React, { Component } from 'react'
        import { Provider } from 'react-redux'
        import { createStore, applyMiddleware } from 'redux'
        import Login from './components/Login/Login';
        import Home from './components/Home/Home';
        import reducers from './reducers'
        import thunk from 'redux-thunk'

        import {Router, Route} from 'react-router-dom'

        import { history } from './utils';

        const store = createStore(reducers, applyMiddleware(thunk))



        export default class App extends Component {
          constructor(props) {
            super(props);

            history.listen((location, action) => {
              // clear alert on location change
              //dispatch(alertActions.clear());
            });
          }
          render() {
            return (
              <Provider store={store}>
                <Router history={history}>
                  <div>
                    <Route exact path="/" component={Home} />
                    <Route path="/login" component={Login} />
                  </div>
                </Router>
              </Provider>
            );
          }
        }

export const config = {
    apiUrl: 'http://localhost:61439/api'
};
import { createBrowserHistory } from 'history';

    export const history = createBrowserHistory();
//index.js
export * from './config';
export * from './history';
export * from './Base64';
export * from './authHeader';

import { SHOW_LOADER, AUTH_LOGIN, AUTH_FAIL, ERROR, AuthConstants } from './action_types'

import Base64 from "../utils/Base64";

import axios from 'axios';
import {history, config, authHeader} from '../utils'
import axiosWithSecurityTokens from '../utils/setAuthToken'


export function SingIn(username, password){


    return async (dispatch) => {
      if(username == "gmail"){
        onSuccess({username:"Gmail"}, dispatch);
      }else{
      dispatch({type:SHOW_LOADER, payload:true})
        let auth = {
            headers: {
              Authorization: 'Bearer ' + Base64.btoa(username + ":" + password)
            }
          }
        const result = await axios.post(config.apiUrl + "/Auth/Authenticate", {}, auth);
        localStorage.setItem('user', result.data)
        onSuccess(result.data, dispatch);
    }
  }

}

export function GetUsers(){
  return async (dispatch) => {
var access_token = localStorage.getItem('userToken');
    axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}` 

    var auth = {
      headers: authHeader()
    }
    debugger
      const result = await axios.get(config.apiUrl + "/Values", auth);
      onSuccess(result, dispatch);
      dispatch({type:AuthConstants.GETALL_REQUEST, payload:result.data})
  }
}



const onSuccess = (data, dispatch) => {

  const {username} = data;
  //console.log(response);
  if(username){
    dispatch({type:AuthConstants.LOGIN_SUCCESS, payload: {Username:username }});
    history.push('/');
    // Actions.DashboardPage();
  }else{
    dispatch({ type: AUTH_FAIL, payload: "Kullanici bilgileri bulunamadi" });
  }
  dispatch({ type: SHOW_LOADER, payload: false });
}
const onError = (err, dispatch) => {
  dispatch({ type: ERROR, payload: err.response.data });
  dispatch({ type: SHOW_LOADER, payload: false });
}

export const SingInWithGmail = () => {
  return { type :AuthConstants.LOGIN_SUCCESS}
}

export const SignOutGmail = () => {
  return { type :AuthConstants.LOGOUT}
}
Kumamoto answered 26/6, 2019 at 9:34 Comment(0)
E
0

while using react-router-dom version +5 you can't use useHistory hook in redux(redux toolkit).

So if you want to redirect after an action is dispatched you can get your history "via useHistory() hook" in your current page(component) then pass the history along with your payload as an argument to redux. Hence you can easily manage your history in redux after an action is dispatched like this : history.push ("somewhere)

Exodontics answered 8/12, 2021 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.