Redirect user with React-Router 4 and Redux-Promise

954 Views Asked by At

I have a simple use case which within the React-Router 4 and Redux environment seems pretty difficult to achieve.

When a user logs in, a redux promise action is fired off which returns an API token, expiry date and some other info. When and if the promise is fulfilled I would then want to redirect the user to another page.

I cannot for figure out how to redirect a user programmatically using the above architecture. I want to do it within Action.js, but it feels that it can only be done in Reducers.js. Even though I am not sure even how to start.

Best way I have found so far is to have a Redirect component in my view, and have the promise set the loggedIn state to true, if the loggedIn prop is true on the view it will then return the redirect prop which will then redirect the user to the desired page. Somehow this feels like a lot of extra code for a simple use case.

Any advice would be helpful. Thank you.

Action.js:

import axios from 'axios';

export function login(email, password) {
  return {
    type: 'LOGIN_USER',
    payload: axios.post('http://localhost:3114/api/users/authenticate', 
    {
      email,
      password,
    }),
  };
}

Reducers.js

// Promise
// pending
// fulfilled
// rejected
const initalState = {
  token: null,
  expiryDate: '',
  loading: false,
  error: null,
};

// REDCUER
function loginReducer(state = initalState, action) {
  switch (action.type) {
    case 'LOGIN_USER_PENDING':
      return { ...state, loading: true };
    case 'LOGIN_USER_FULFILLED':
      return {
        ...state,
        loading: false,
        token: action.payload.data.token,
        expiryDate: action.payload.data.expires,
        loggedIn: true,
      };
    case 'LOGIN_USER_REJECTED':
      return { ...state, loading: false, error: `${action.payload}` };
    default:
      return state;
  }
}

export default loginReducer;

LoginView.js

render() {
        const { data, login, loggedIn } = this.props;
        if (data.loggedIn) {
          return <Redirect to="/users" push />;
        }
        return (
          ...
        )
       }

Regards, Emir

1

There are 1 best solutions below

0
On

I could see a isLoggedIn in the Redux state potentially being useful at a later point (for example, you could wrap all routes / pages that should only be visible for logged in users in a component that checks this isLoggedIn and potentially redirects away to the login page), so I wouldn't necessarily change anything there.

But if you still want to find another way around this then it is possible to redirect programamtically (without rendering a Redirect component) by using history.push(newUrl) or history.replace(newUrl). But I'm not sure where you would call it. Having side-effects in the action creator seems slightly less weird to me, compared to having it in the reducer, so one possibility would perhaps be to do something like

import axios from 'axios';

// pushOnResolve would be something like () => push('mynew/url')
export function login(email, password, pushOnResolve) {
  const promise = axios.post('http://localhost:3114/api/users/authenticate';
  // it's possible to have several .then on a promise
  // so this shouldn't interfere with
  // the Redux-promise or the LOGIN_USER_FULFILLED reducer
  promise.then(pushOnResolve);

  return {
    type: 'LOGIN_USER',
    payload: promise, 
    {
      email,
      password,
    }),
  };
}