5
votes

I'm working on a personal project and I have trouble with checking if a user is logged in or not so that when they try to go to /login, it'll redirect them to the home page. Currently what my code does is the following:

  1. Defined my own Route components
  2. When checkIfSignedIn is called, it dispatches actions that set the isLoggedIn state to either True or False.
  3. The AuthenticatedRouter component checks if the store's isLoggedIn state is true or false and will either render the component or redirect to home.
  4. This works, but the issue is that isLoggedIn is initially false, so if they are logged in, the login form shows up for a second and then redirects after isLoggedIn becomes true.

I can't figure out how to fix the problem with rendering the component with the default state and then suddenly redirects because the state changes. Thanks

  • I'm using firebase for the user account.

Routes.js

const Routes = (props) => {
    props.checkIfSignedIn();
    return(
        <Router>
            <Switch>
                <Route exact path='/' component={App}/>
                <AuthorizedRoute path="/signup" component={SignUp}/>
                <AuthorizedRoute path="/signin" component={SignIn}/>
                <Route component={InvalidPage}/>
            </Switch>
        </Router>
    );
};

const mapDispatchToProps = (dispatch) => {
    return{
        checkIfSignedIn: () => dispatch(checkIfSignedIn())
    };
};

export default connect(null, mapDispatchToProps)(Routes);

AuthorizedRoute.js

class AuthorizedRoute extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            isLoggedIn: false
        };
    };

    render(){
        const { component: Component, ...rest } = this.props;
        return(
            <Route {...rest} render={props => {
                if (this.props.isLoggedIn){
                    console.log(this.state.isLoggedIn);
                    return <Redirect to="/" />
                } else{
                    return <Component {...props} />
                }
            }} />
        );
    }
};


const mapStateToProps = (state) => {
    return{
        isLoggedIn : state.authentication.isLoggedIn
    };
};

export default connect(mapStateToProps, null)(AuthorizedRoute);

authentication.js - action creators

const setSignedInFalse = () => {
    return{
        type: IS_SIGNED_IN_FALSE
    };
};

const setSignedInTrue = () => {
    return{
        type: IS_SIGNED_IN_TRUE
    };
};

const checkIfSignedIn = () => {
    return dispatch => {
        firebaseApp.auth().onAuthStateChanged(user => {
            if (user){
                dispatch(setSignedInTrue());
            } else{
                dispatch(setSignedInFalse());
            }
        });
    };
};

export default checkIfSignedIn;

authentication.js - reducer

const defaultState = {
    isLoggedIn: false
};

const authentication = (state = defaultState, action) => {
    let authenticationState = null;
    switch (action.type){
        case IS_SIGNED_IN_FALSE:
            authenticationState = {
                isLoggedIn: false
            };
            return authenticationState;
        case IS_SIGNED_IN_TRUE:
            authenticationState = {
                isLoggedIn: true
            };
            return authenticationState;
        default:
            return state;
    };
};

export default authentication;

Hacky solution (Not sure if this is frowned upon).

I set my Auth reducer's default state, isLoggedIn : null instead of false. Then in my AuthorizedRoute component I now have 3 conditions to render null first and then either the component or redirect.

//AuthorizedRoute Component
if (this.props.isLoggedIn){
    return <Redirect to="/" />
} else if (this.props.isLoggedIn === false){
    return <Component {...props} />
} else{
    return null;
}
2
I personally don't frown upon your 'hacky solution'. There are 3 states, loggedIn=true, loggedIn=false, loggedIn=uninitialized. Render null when you don't know what to render (or a loading... indicator). - brub

2 Answers

2
votes

What you may do is something like this: (in your auth reducer).

let user = JSON.parse(localStorage.getItem('user'));
const initialState = user ? { isLoggedIn: true } : {};

Also you had to set the localstorage when your user is logging in and remove it when he is logging out.

2
votes
class AuthorizedRoute extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isLoggedIn: false
        };
    };

    render() {
        const { component: Component, ...rest } = this.props;
        const user = JSON.parse(localStorage.getItem('user'));
        return (
            <Route { ...rest } render={ props => user ? <Component {...props} /> : <App /> } />
        );
    }
};


const mapStateToProps = (state) => {
    return {
        isLoggedIn : state.authentication.isLoggedIn
    };
};

export default connect(mapStateToProps, null)(AuthorizedRoute);