Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React + NextJS - Protected routes

Objective : I want to redirect a logged in user to the home page if he/she tries to manually go to the /auth/signin.

Signin page/component :

const Signin = ({ currentUser }) => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const { doRequest, errors } = useRequest({
        url: '/api/users/signin',
        method: 'post',
        body: {
            email, password
        },
        onSuccess: () => Router.push('/')
    });

    useEffect(() => {
        const loggedUser = () => {
            if (currentUser) {
                Router.push('/');
            }
        };
        loggedUser();
    }, []);

Custom _app component:

const AppComponent = ({ Component, pageProps, currentUser }) => {
    return (
        <div>
            <Header currentUser={currentUser} />
            <Component {...pageProps} currentUser={currentUser} />
        </div>

    )
};

AppComponent.getInitialProps = async (appContext) => {
    const client = buildClient(appContext.ctx);
    const { data } = await client.get('/api/users/currentuser');
    let pageProps = {};
    if (appContext.Component.getInitialProps) {
        pageProps = await appContext.Component.getInitialProps(appContext.ctx);
    }
    return {
        pageProps,
        ...data
    }
};

export default AppComponent;

Issue :

I tried this, but this causes a slight delay as it won't be server side rendered. By delay I mean: It shows the page I don't want to show for a second or so before redirecting.

I could use a loading sign or bunch of if else conditions, but that would be a work around, what would be the best approach/practice to handle this issue?


Another solution I came up with:

  • I built a redirect hook :
import Router from 'next/router';
export default (ctx, target) => {
    if (ctx.res) {
        // server 
        ctx.res.writeHead(303, { Location: target });
        ctx.res.end();
    } else {
        // client
        Router.push(target);
    }
}
  • Then I made 2 HOCs(for logged in and logged out user) to for protected routes:
import React from 'react';
import redirect from './redirect';
const withAuth = (Component) => {
    return class AuthComponent extends React.Component {
        static async getInitialProps(ctx, { currentUser }) {
            if (!currentUser) {
                return redirect(ctx, "/");
            }
        }
        render() {
            return <Component {...this.props} />
        }
    }
}
export default withAuth;
  • Then I wrapped the components with it for protecting the page:
export default withAuth(NewTicket);

Is there any better approach to handling this? Would really appreciate the help.

like image 268
Karan Kumar Avatar asked Aug 04 '20 16:08

Karan Kumar


People also ask

How do you protect routes in React?

To protect routes, the private components must also have access to the isLoggedIn value. You can do this by creating a new component that accepts the isLoggedIn state as a prop and the private component as a child. For instance, if your new component is named "Protected", you would render a private component like this.

Does NextJs use client side routing?

The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application. A React component called Link is provided to do this client-side route transition.


1 Answers

export const getServerSideProps = wrapper.getServerSideProps(
  (store) =>
    async ({ req, params }) => {
      const session = await getSession({ req });

      if (!session) {
        return {
          redirect: {
            destination: '/',
            permanent: false,
          },
        };
      }
    }
);

Here in Next 9++ you can do like this just check the session, if there are none, we can return a redirect with destination to route the user to end point!

like image 70
An in real life Avatar answered Sep 23 '22 04:09

An in real life