18

I have the following react functional component to help support authentication required routes with react-router.

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    isAuthenticated() ? ( 
        <Component {...props}/>
    ) : (
        <Redirect to={{
            pathname: '/login', 
            state: {from: props.location }
        }}/>
    )
  )}/>
)

I need to convert this from a functional component to a class component so I can take advantage of the componentDidMount method of React.Component. Unfortunately I'm having trouble figuring out how to migrate this. If I take it as is I need to replicate the Component and ...rest parameters, but I'm not sure how to do that. I think I can get the Component parameter with this.props.component, but I'm not sure how to pull ...rest. I'm new to JSX and ES6 so any help or guidance would be much appreciated.

1
  • can you show the code with use of this functional component ? Commented Apr 29, 2017 at 12:28

4 Answers 4

27

The functional component is the render function, therefore:

class PrivateRoute extends React.Component {
    render() {
       const {component: Component, ...rest} = this.props;

       return (
           <Route {...rest} render={props => (
               isAuthenticated() ? ( 
                 <Component {...props}/>
           ) : (
            <Redirect to={{
                pathname: '/login', 
                state: {from: props.location }
            }}/>
           )
         )}/>
       );
    }
}

or, written a bit more readable:

class PrivateRoute extends React.Component {
    render() {
       const {component: Component, ...rest} = this.props;

       const renderRoute = props => {
           if (isAuthenticated()) {
              return (
                  <Component {...props} />
              );
           }

           const to = {
               pathname: '/login', 
               state: {from: props.location}
           };

           return (
               <Redirect to={to} />
           );
       }

       return (
           <Route {...rest} render={renderRoute}/>
       );
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

The thing i don't understand is const {component: Component, ...rest} = this.props;. What does this do, it's like a reversed variable?
@Mr. Bear: This is destructuring assignment
1

A nice, clean refactor by extending the Route component:

class PrivateRoute extends Route {

    render() {
        return isAuthenticated()
            ? super.render()
            : <Redirect to={{
                pathname: '/login',
                state: {from: props.location}
            }}/>;
    }
}

If you use this, you have to wrap your <PrivateRoute/>s in a <Switch/>, as below. Otherwise, you will have duplicate redirects and the page will fail to load.

<Router>
    <Navbar/>
    <SideDrawer/>
    <Switch>
        <Route              path="/tokens"   component={Login}/>
        <PrivateRoute exact path="/"         component={ExampleComponent}/>
        <PrivateRoute       path="/users"    component={Users}/>
        <PrivateRoute       path="/software" component={Software}/>
    </Switch>                   
</Router>

Comments

0

For your case useEffect hook will be the best choice.

You can simply add use effect hook to your component

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

useEffect(()=>{
  // your logic for componentDidMount here
}, []);

return (
  <Route {...rest} render={props => (
    isAuthenticated() ? ( 
        <Component {...props}/>
    ) : (
        <Redirect to={{
            pathname: '/login', 
            state: {from: props.location }
        }}/>
    )
  )}/>
)
}

Comments

0

@Sulthans answer is correct and directly answers your question. But I would like to put the answer differently with a suggestion.

Based on your question your requirement is something like using life cycle hooks ie componentDidMount in your functional component.

I would like to suggest continuing to use the functional component and getting the hook implemented using useEffect provided if you are using the latest react

useEffect(() => {
  console.log('mount it!');
}, []); // passing an empty array as second argument triggers the callback in useEffect only after the initial render thus replicating `componentDidMount` lifecycle behaviour

I recommend this due to the following reasons

  • It reduces the boilerplate
  • It promotes readability
  • React team nowadays promotes the use of function components. Recommend reading gradual adoption strategy.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.