Reputation: 157
So I have a small Next.js app, which has pages only allowed for logged in users. Nothing special there. To manage the authenticated routes I wrote an HOC (called withAuth) that redirects on the server, to avoid pages flashing on the client. It works great so far, but I have the issue, that my app is internationalized. So currently the user only gets redirected to the login in the default language (English), but I would prefer to keep their language selected.
Unfortunately, useRouter() is only available on the client, so my first approach did not succeed (I should have known better). This is my code:
import redirect from "../utils/redirect";
import * as React from "react";
import { sendData } from "../utils/sendRequest";
export const withAuth = <T extends object>(C: React.ComponentClass<T>) => {
return class AuthComponent extends React.Component<T> {
static async getInitialProps({...ctx}: NextPageContext) {
await sendData('GET','/user').then(response => {
if (!response || !response.success) {
redirect(ctx, "/login");
return {
user: null,
};
}else return {
user: response.user,
};
}).catch(err=>{console.error(err)})
}
render() {
return <C {...this.props} />;
}
};
};
import Router, {useRouter} from "next/router";
const redirect = (context: any, target: string) => {
let lang = "";
/*if(!(useRouter.locale == "en")){
lang = "/"+useRouter.locale; This wont work
}*/
if (context.res) {
context.res.writeHead(303, { Location: /*lang+*/target });
context.res.end();
} else {
Router.replace(/*lang+*/target);
}
};
export default redirect;
import * as React from "react";
import { withAuth } from "../components/auth";
//...
class ProfileClass extends React.PureComponent<{}> {
render() {
return <Profile />;
}
}
export default withAuth(ProfileClass);
I am greatful for any ideas how to solve this problem :) Thanks!
Upvotes: 2
Views: 5782
Reputation: 50278
getInitialProps
in _app
page.You can access a router
instance from the getInitialProps
context object that can in turn access the current locale. Note that the router
object is only available to the context passed to _app
's getInitialProps - but not available to the context passed to the other pages.
// pages/_app.tsx
static async getInitialProps({ router, ctx }: NextPageContext) {
const lang = router.locale;
await sendData('GET','/user').then(response => {
if (!response || !response.success) {
redirect(ctx, `${lang}/login`);
return {
user: null,
};
} else {
return {
user: response.user,
};
}
}).catch(err => { console.error(err) })
}
getServerSideProps
for other pages.Since locale
information can't be accessed from a page with getInitialProps
, you can use getServerSideProps
instead. Doing so involves refactoring the existing logic to fit into getServerSideProps
implementation.
First, we'll repurpose withAuth
and separate the logic for getting the user. This will add the user
object to the context
and pass it to getServerSideProps
.
// /components/auth
export function withAuth(gssp) {
return async (context: GetServerSidePropsContext) => {
try {
const response = await sendData("GET", "/user");
if (response && response.success) {
// Add user to context to be used in `getServerSideProps`
context = { ...context, user: response.user };
}
} catch (err) {
console.error(err);
}
return await gssp(context); // Pass `context` and continue on to call `getServerSideProps`
};
}
Then, withAuth
can be used to wrap the getServerSideProps
function in your page, where the redirect will happen if the user
isn't set.
import * as React from "react";
import { withAuth } from "../components/auth";
export const getServerSideProps: GetServerSideProps = withAuth(async (context: : GetServerSidePropsContext) => {
if (!context.user) {
return {
redirect: {
destination: `/${context.locale}/login`,
statusCode: 303
}
};
}
return {
props: {
user: context.user
}
};
});
class ProfileClass extends React.PureComponent<{}> {
render() {
return <Profile />;
}
}
export default ProfileClass;
Upvotes: 2