Use React HOC component inside another component JSX block with TypeScript

Viewed 101

I have a Higher Order React component (HOC) which I'm trying to use inside another functional component, wrapping its children:

// HOC
const withSuperpower = <P extends object>(Component: React.ComponentType<P>) => {
  // do some stuff...
  return (hocProps: P) => {
    return (
      <>
        <div id="superpower" />
        <Component {...hocProps} /> 
      </>
    );
  };
};

// functional component
const = Buddy: React.FC<{children: JSX.Element}>= ({children}) => {
  // do some stuff...
  return (
    <div id="buddy">
      { withSuperpower(<>children</>) } {/* this doesn't compile  */}
    </div>
  );
};

The previous code doesn't compile. It seems that only the later works, when I'm wrapping the component's function/class reference:

export const superBuddy = withSuperpower(Buddy); {/* only this works  */}

So to make the first piece of code work I have to create 3rd component which seems to me unnecessary redundant:

const HOCUser:React.FC<{children: React.ReactNode}> = ({children}) => {
  return <>{children}</>;
}

const Buddy: React.FC<{children: JSX.Element}> = ({children}) => {
  return (
    <div id="buddy">
      { withSuperpower(HOCUser) } {/* this works fine  */}
    </div>
  );
}

Is there a better way to use HOC inside functional component's JSX tags block?

2 Answers

The problem is that your HOC expects React component as a parameter and you pass JSX Element to it.

So you can either adjust the HOC parameter type to JSX.Element, but this will be unconventional use of HOC, as HOCs typically wrap React components.

Alternative option would be to use a normal component instead of HOC:

<Superpower><>children</></Superpower>

and then to turn your HOC from what you have:

// HOC
const withSuperpower = <P extends object>(Component: React.ComponentType<P>) => {
  // do some stuff...
  return (hocProps: P) => {
    return (
      <>
        <div id="superpower" />
        <Component {...hocProps} /> 
      </>
    );
  };
};

to:

// HOC

  const Superpower = ({ children } : P) => {
        // do some stuff...
        return (
          <>
            {children}
          </>
        );
      };
    };

as you can see, the structure is simplified and we get rid of the wrapper HOC function, and still can extend it with custom logic.

You may also consider using hooks instead of HOCs.

I ended up with the following solution:

const WithSuperpowerComp: React.FC<{children: JSX.Element;}> = ({value,children}) => {
  const UseHOC = withSuperpower(() => <>{children}</>);
  return (
    <div id="testComponent">
      <UseHOC>{children}</UseHOC>
    </div>
  );
};

and then using WithSuperpowerComp in the code is easy:

<WithSuperpowerComp>
  <div>This is a test, just a test</div>
</WithSuperpowerComp>
Related