Home > Blockchain >  Next.js | HOC from REST API with Typescript not being read when wrapping a child component to access
Next.js | HOC from REST API with Typescript not being read when wrapping a child component to access

Time:08-24

I have this HOC line of code from withUser.tsx. When a user is authenticated, the authenticated pages will then be wrapped by it so that the specified user-role will be the one to only have access to pages intended.

import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";

const withUser = (Page: any) => {
  const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
  WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
    const token = getCookie("token", context.req);
    let user = null;
    let userLinks = [];

    if (token) {
      try {
        const response = await axios.get(`${API}/user`, {
          headers: {
            authorization: `Bearer ${token}`,
            contentType: "application/json",
          },
        });

        console.log("Response in withUser: ", response);
        user = response.data.user;
        userLinks = response.data.links;
      } catch (err: unknown) {
        const error = err as AxiosError;
        if (error.response?.status === 401) {
          user = null;
        }
      }
    }

    if (user === null) {
      // redirect
      context.res.writeHead(302, {
        Location: "/",
      });
      context.res.end();
    } else {
      return {
        ...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
        user,
        token,
        userLinks,
      };
    }
  };

  return WithAuthUser;
};

export default withUser;

Now, the above code is not my final writing of TypeScript, I could be wrong but this is how I converted it from JS, please feel free to give me a refactored TSX codes here, here is the JS version:

import axios from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";

const withUser = (Page) => {
  const WithAuthUser = (props) => <Page {...props} />;
  WithAuthUser.getInitialProps = async (context) => {
    const token = getCookie("token", context.req);
    let user = null;
    let userLinks = [];

    if (token) {
      try {
        const response = await axios.get(`${API}/user`, {
          headers: {
            authorization: `Bearer ${token}`,
            contentType: "application/json",
          },
        });

        console.log("Response in withUser: ", response);
        user = response.data.user;
        userLinks = response.data.links;
      } catch (error) {
        if (error.response.status === 401) {
          user = null;
        }
      }
    }

    if (user === null) {
      // redirect
      context.res.writeHead(302, {
        Location: "/",
      });
      context.res.end();
    } else {
      return {
        ...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
        user,
        token,
        userLinks,
      };
    }
  };

  return WithAuthUser;
};

export default withUser;

But now, when using it when an Authenticated /user page, I could not get any data from the user. It will give me an undefined and for example, user.first_name will not be shown:

import withUser from "../withUser";

const User = ({ user }: any): JSX.Element => (
  <div className="flex min-h-screen flex-col items-center justify-center">
   {user.first_name}
  </div>
);

export default withUser(User);

Any correct ways of implementing this would be very much appreciated. Thanks!

CodePudding user response:

Whether you are in Reactjs or Nextjs, I think there needs to have a correct type definitions of your HOC component in the first place.

First you need to define your HOC component as a React.ComponentType:

const withUser = (ChildComp: React.ComponentType<any | string>) => { /* code follows */ }

you also need to define an interface for the expected values for these"

const token = getCookie("token", context.req);
let user = null;
let userLinks = [];

and when you wrap your child component, say user.tsx, do it like this:

type UserType = {
  first_name: string
}
const User: React.SFC<ContainerProps> = ({ user}: UserType)=> (
  <h1>{user.first_name ?? "User not found"}</h1>
);

export default withUser(User);

You can read more about here: Create a TypeScript HOC in React

CodePudding user response:

Okay, sorry this was just a bug and I figure out that I did not have any userLinks from the REST API that I was passing in. So I can already consider this question as resolved as I have already fixed it.

Here is the code of my fix:

import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";

const withUser = (Page: any) => {
    const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
    WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
        const token = getCookie("token", context.req);
        console.log("token: ", token);

        let user = null;

        if (token) {
            try {
                const response = await axios.get(`${API}/user`, {
                    headers: {
                        authorization: `Bearer ${token}`,
                        contentType: "application/json",
                    },
                });

                console.log("response: ", response);
                user = response.data;
            } catch (err: unknown) {
                const error = err as AxiosError;
                if (error.response?.status === 401) {
                    user = null;
                }
            }
        }

        if (user === null) {
            // redirect
            context.res.writeHead(302, {
                Location: "/",
            });
            context.res.end();
        } else {
            return {
                ...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
                user,
                token,
            };
        }
    }

    return WithAuthUser;
}
export default withUser;
  • Related