Nats
Nats

Reputation: 170

React router private route not redirecting

I've got a private route which redirects to an Auth component if there is nothing in local storage. At the moment it's not taking the storage into account as it takes me to the Auth page when there is local storage data and when there isn't. I am using redux in the application but not for this part yet, I wanted to get it working with a basic set up first.

I think may be my component hasn't had time to reload and register that there's a storage item. I can see it trying to navigate away but comes straight back to Auth. Can anyone help?

My private route:

export const ProtectedRoute = ({ component: Component, ...rest }: any) => {

    const dispatch = useDispatch();
    const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);


    useEffect(() => {

        const userData: any = localStorage.getItem('userData');

        if (!userData) {
            setIsAuthenticated(false);
        }

        setIsAuthenticated(true);



    }, [dispatch])


    return (

        <Route {...rest} render={(props: any) => 
            isAuthenticated ?
                <Component {...props} />
                : <Redirect to="/auth" />
        } />
    );
};

Component containing my routes:


export const Main = () => {

    return (
        <>
            <MainContainer className="MainContainer">
                <Switch location={location || background}>
                    <Route exact path="/">
                        <Home />
                    </Route>
                    <ProtectedRoute /*isAuthenticated={isAuthenticated}*/ component={Nativers} path="/nativers" exact />
                </Switch>
            </MainContainer>

        </>
     )
 }

The auth component function handling any redirecting when you click on the login/sign up button:

    const authHandler = async () => {

        let action;
        if (isSignUp) {

            action = AuthActions.signup(
                formState.inputValues.name,
                formState.inputValues.email,
                formState.inputValues.password,
                formState.inputValues.repeatPassword
            )
        } else {
            action = AuthActions.login(
                formState.inputValues.email,
                formState.inputValues.password
            )
        }
        setError(null);
        setIsLoading(true);
        try {
            await dispatch(action);
            return <Redirect to="/nativers" />;
        } catch (err) {
            setError(err.message);
            setIsLoading(false); 
        }
    }

Upvotes: 0

Views: 1338

Answers (1)

Tunmise Ogunniyi
Tunmise Ogunniyi

Reputation: 2573

When the page path is updated, the matching Route component(s) is called to render whatever component was registered with it. The right place to put the logic to either redirect or not depending on the availability of localStorage data should be inside the Route component because that’s where it is guaranteed to be executed before render of the registered component.

However, in your code, this logic is placed outside the Route component and even though you are trying to use dispatch to update the value of isAuthenticated variable, the update would only happen if the ProtectedRoute component is re-rendered because of the way the useEffect hook is setup.

There are multiple ways to fix this, but given what you already have, I think the easiest would be to do some thing like this:

export const ProtectedRoute = ({ component: Component, ...rest }: any) => {
  return (
      <Route {...rest} render={(props: any) => 
        localStorage.getItem('userData') ?
              <Component {...props} />
              : <Redirect to="/auth" />
      } />
  );
};

Now, the logic to redirect or not lives inside the Route component, consequently, whenever, there is page path matches that of the Route component, the logic is executed and a redirect happens if needed.

Upvotes: 2

Related Questions