ScreamX
ScreamX

Reputation: 339

Private Route - cannot be invoked without 'new'

I am trying to do a token check when using Route. I liked one solution, but for some reason it does not want to work and gives an error:

Class constructor App cannot be invoked without 'new'

const RequireAuth = (Component) => { 
    return class App extends Component { 
        componentWillMount() { 
            const getToken = localStorage.getItem('token'); 
            if(!getToken) { 
               this.props.history.replace({pathname: '/'}); 
            } 
        } 
        render() { 
           return <Component {...this.props} /> 
        }
    } 
} 

export { RequireAuth };

Usage:

...
<PrivateRoute path="/profile" component={RequireAuth(Profile)} />
...

I have no idea why it is coming out and how to fix it. Have a clue?

Upvotes: 0

Views: 222

Answers (3)

Drew Reese
Drew Reese

Reputation: 202801

You may be masking the react Component type. Try changing the name of the parameter, or change to functional component if possible. Also, componentWillMount is soon to be deprecated, you really should use componentDidMount instead.

import React, { Component } from "react";

const requireAuth = (component) => { // lowercase to not conflict with React.Component!
  return class App extends Component { 
    componentDidMount() { 
      const getToken = localStorage.getItem('token'); 
      if(!getToken) { 
        this.props.history.replace({pathname: '/'}); 
      } 
    }

    render() { 
      const Component = component; // convert to PascalCase for react use
       return <Component {...this.props} /> 
    }
  } 
} 

export { requireAuth };

or

import React, { useEffect } from "react";

const requireAuth = (Component) => { 
  return props => {
    useEffect(() => {
      const getToken = localStorage.getItem('token'); 
      if(!getToken) { 
         props.history.replace({pathname: '/'}); 
      } 
    }, []); // empty dependency array runs once on mount

    return <Component {...this.props} />;
  } 
} 

export { requireAuth };

I should note though that react's convention for Higher Order Components is to camelCase the name and not render them directly, and instead decorate an existing component.

  • const RequiresAuthMyComponent = requireAuth(MyComponent);
  • export default requireAuth(MyComponent);

Upvotes: 1

Domino987
Domino987

Reputation: 8774

You can simply transform it to a functional component, which can be called instead of a class. And instead of componentWillMount use useEffect with an empty dependency array.

const RequireAuth = Component => props => {
  useEffect(() => {
    const getToken = localStorage.getItem("token");
    if (!getToken) {
      props.history.replace({ pathname: "/" });
    }
  }, []);
  return <Component {...props} />;
};

Upvotes: 1

Jayesh Naghera
Jayesh Naghera

Reputation: 339

I'm sure javascript class is not call such as you have mention (App Class).

So you can try something like below:

const RequireAuth = (Component) => { 
    class App extends Component { 
        componentWillMount() { 
            const getToken = localStorage.getItem('token'); 
            if(!getToken) { 
               this.props.history.replace({pathname: '/'}); 
            } 
        } 
        render() { 
           return <Component {...this.props} /> 
        }
    } 
    return new App();
} 

export { RequireAuth };


Upvotes: 1

Related Questions