Andy
Andy

Reputation: 735

Cant figure out how to create a 'basic' HOC for a react / NextJS application

I have built an application that works well, but i now need to add some logic into each page that checks to see if the user has a subscription and if they do then send them to payment page to make a payment.

I have a hook (using SWR) that gets me the user session and within the hook it returns a boolean for isSubscribed. I intend to use this.

const session = useSession();

if(session.isLoading) {
   return <></>;
}

if(!session.isSubscribed) {
   /* redirect the user*/
} 

return (
  <p>HTML of the page / component</p>
)

An above example is what i currently do. But this solution requires me to copy pasta everytime to the page which obviously i can do, but it's no way efficient. I know that HOC exists and from what i know i an use a HOC to do this. But i have no idea how to write one that would fit this purpose.

As an added benefit, it would be useful to add the session as a prop to the 'new component' so that i dont have to call the hook twice.

Thanks for all and any help.

p.s. i mention it in the title, but i'm using NextJS. Not sure if this has any baring (i dont think it does, but worth mentioning)

Upvotes: 1

Views: 1875

Answers (3)

Sergey Karavaev
Sergey Karavaev

Reputation: 1761

I think this problem doesn't necessary require a HOC, but can be solved with a regular component composition. Depending on your actual use case, it may or may not be a simpler solution.

We could implement a Session component that would leverage the useSession hook and conditionally render components passed via the children prop:

const Session = props => {
    const { isLoading } = useSession();
    if (isLoading) {
        return "Loading...";
    }

    return props.children;
};

Then nest the Page component into the Session:

const GuardedPage: React.FC<PageProps> = props => {
    return (
        <Session>
            <Page {...props} />
        </Session>
    );
};

I see the question has already been answered, just wanted to suggest an alternative. One of the benefits of this approach is that we can wrap an arbitrary tree into the Session, and not just the Page.

Upvotes: 1

DevSkyWalker
DevSkyWalker

Reputation: 146

Are you trying to return a page loading screen component and direct the user to the appropriate page based on thier subscription status? or isLoading handles one event and isSubscribed handles another?

Let's define (HOC) higher order component for the sake of your problem. By using HOC, logic can be modularized and redistributed throughout components. This HOC your creating should have the capability to call different methods on a single data source or one method to be applied across multiple components. For instance say you have an API component with 5 end points (login, subscribe, logout, unsubsubscribe) the HOC should have the ability to utilize any of the endpoints from any other component you use it in. HOC is used to create an abstraction that will allow you to define logic in a single place.

Your code calls one singular method to check if the session is in use of display the content of a page based on user subscription and page loading. Without seeing the components you are trying to use I can not determine the state that needs to be passed? but I will give it shot.

const session = useSession();

if(session.isLoading) {
   return <></>;
}

if(!session.isSubscribed) {
   /* redirect the user*/
} 

return (
  <p>HTML of the page / component</p>
) 

First thing I see wrong in above code as a use case for an HOC component you have no export statement to share with other components. Also, why use 2 return statements for isLoading unless both conditions need to be checked (isLoading & isSubscribed) also, are these conditional statements depended on each other or seprate functions that can be called separately from another source? if you posted more of your code or the components you are pasting this into it would help?

To use this as an HOC in NEXT is essentially the same as react. Dependant logic

const session = useSession(props);
// ad constructor and state logic 
... 

if(session.isLoading) {
   return this.setState({isLoading: true});
} else { 
  return this.setState({isSubscribed: false});
}

Separate logic

const session = useSession(props);
// ad constructor and state logic 
... 

isLoading () => { 
  return this.setState({isLoading: true});
} 


isSubscribed() => { 
  return this.setState({isSubscribed: true});
} 

or something like this that uses routes...

import React, { Component } from 'react';
import { Redirect, Route } from 'react-router-dom';

export const HOC = { 

  isState: false, 

  isSubscribed(props) {
    this.isState = false;
    setTimeout(props, 100);
  },
  isLoading(props) {
    this.isState = true;
    setTimeout(props, 100);
  }
};

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

  return (
  <Route {...rest} render={(props) => (
     HOC.isAuthenticated === true ? <Component {...props} /> : <Redirect to='/' /> 
  )}/>
)};


}

If you could share more of you code it would be more helpful? or one of the components you are having to copy and paste from your original HOC code. I would be easier than stabbing in the dark to assist in your problem but I hope this helps!

Cheers!

Upvotes: 0

choz
choz

Reputation: 17888

You can create a wrapper HOC such as following;

const withSession = (Component: NextComponentType<NextPageContext, any, {}>) => {
  const Session = (props: any) => {
    const session = useSession();

    if (session.isLoading) {
      return <>Loading..</>
    }
    else {
      return <Component {...props} />
    }
  };

  // Copy getInitial props so it will run as well
  if (Component.getInitialProps) {
    Session.getInitialProps = Component.getInitialProps;
  }

  return Session;
};

And to use it in your page or component, you can simply do like;

const UserDetailPage: React.FC = (props) => {
  // ...
  // component's body
  return (<> HI </>);
};

export default withSession(UserDetailPage);

Upvotes: 3

Related Questions