3

Yes, I realize this question has been asked, but none of the answers I found resolved this. I am writing a simple higher order component in Typescript to verify authorization before rendering a component. So far it looks like this:

export function withAuth(Component: React.ComponentType) {

    if (!Component) return null;

    useEffect(() => {
        verifyToken().then(res => console.log(res))
    }, []);

    return (
        <Component/>
    )
}

I have a larger FunctionComponent called EditorContainer that I pass to the HOC and export from its own file: export default withAuth(EditorContainer);

Which is imported as import EditorContainer from "./modules/Editor/containers/EditorContainer"; and throws this error.

I have tried:

  1. Passing the HOC a new instance of the component instead of its constructor. This throws a different error.
  2. Changing or removing all types. The error remains.
  3. Updating react, react-dom, @types/react and @types/react-dom.
  4. Capitalizing withAuth as WithAuth (I'm running out of ideas here).
  5. Removing the component from its original location (being rendered by a React Router route). Makes no difference.

It seems like writing a higher order component in TypeScript is disallowed.

Chris B.
  • 5,477
  • 1
  • 12
  • 30

2 Answers2

1

Fixed this by defining the HOC as a curried function. :

export const withAuth = (Component: ComponentType<any>) => (props: any) => {

    const AuthWrapper: FunctionComponent = (props: any) => {
        const [auth, setAuth] = useState<any>(null);

        useEffect(() => {
            verifyToken().then(res => {
                console.log(res);
                setAuth(res);
            })
        }, []);

        if (!auth) return <Result
            status="403"
            title="403"
            subTitle="Sorry, you are not authorized to access this page."
            extra={<Link to="/"><Button type="primary">Back Home</Button></Link>}
        />;

        return (
            <Component {...props} authUser={auth}/>
        )
    }

    return <AuthWrapper {...props}/>;

};

Literally no idea why this works, so I guess the question's not really answered. How should explicitly returning a function be any different from returning a FunctionComponent which... is a function? Especially after stripping the types, I'm not clear on what the difference is.

Chris B.
  • 5,477
  • 1
  • 12
  • 30
  • This works because an HOC's input is a component and the output needs to be a component. this works now because you are returning a stateless function which is returning a hook. :) . The stateless function is a valid react component. – locomotif Aug 24 '19 at 04:26
  • Thanks for your reply. Maybe I'm being a bit thick, but regardless of whether or not the return value is a function, it's ultimately just returning a React element. So if I set the return type of the HOC as a ReactElement, why will it accept an anonymous function that does nothing but return an element, but not a direct return of the element? – Chris B. Aug 24 '19 at 21:22
  • The HOC would not return a ReactElement. The HOC would need to return a `ComponentClass

    ` or a `FunctionComponent

    `. In your original post, `withAuth` is not an HOC, it is a `FunctionType

    ` which returns a React Element. `withAuth(EditorContainer)` should return a `ComponentType` to satisfy the contract with react that comes from `export default withAuth(EditorContainer)`. Once you added the anonymous function as the return type of `withAuth`, now you have a valid `ComponentType

    `. The anonymous fn is a valid `ComponentType` because it returns a ReactElement.

    – locomotif Aug 25 '19 at 05:57
  • I think I *almost* understand, but it's not because your answer is lacking. Thanks! – Chris B. Aug 30 '19 at 00:28
1

see my comment as to why your solution is working; however you can remove the extra function.

export function withAuth(Component: React.ComponentType) {

    if (Component == null) { return () => null; }
    return () => {
        useEffect(() => {
            verifyToken().then(res => console.log(res))
        }, []);

        return (
            <Component/>
        )
    };
}
locomotif
  • 151
  • 6