Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting stateless React component having arguments to stateful

Inside my React JS project, I am working on the PrivateRoutes. I have gone through this example of private routing and authenticating using react-router-dom.

https://reacttraining.com/react-router/web/example/auth-workflow

According to this documentation, they have created a PrivateRoute as a stateless component.

But my requirement is to convert it to stateful React component as I want to connect my PrivateRoute component to redux store.

Here is my code.

stateless component

import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';

const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route
      {...rest}
      render={props =>
        auth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Component {...props} action="login"/>
        )
      }
    />
  );

  export default PrivateRoute;

I converted this component to stateful React component like this.

stateful React component

import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';
import {connect} from 'react-redux';

  class PrivateRoute extends React.Component {
    render({ component: Component, ...rest }) {
      return (
        <Route
          {...rest}
          render={props =>
            this.props.customer.isAuthenticated ? (
              <Component {...props} />
            ) : (
              <Component {...props} action="login"/>
            )
          }
        />
      );
    }
  }
  export default connect(state => state)(PrivateRoute);

Here, I am reading the data from redux store to check whether the user is authenticated or not.

But the way I am converting the stateless component to stateful isn't correct.

Am I passing the arguments render({ component: Component, ...rest }) correctly?

Will connecting the PrivateRoute with redux store create any problem with props as state=>state will map state to props as well as ...rest will have props object?

Not sure what is happening inside the code.

Update AppRouter.js

import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import {TransitionGroup, CSSTransition} from 'react-transition-group';
import PrivateRoute from './PrivateRoute';

import HomePage from './../components/HomePage';
import AboutUs from './../components/AboutUs';
import ContactUs from './../components/ContactUs';
import PageNotFound from './../components/PageNotFound';
import RestaurantList from '../components/RestaurantList';
import RestaurantMenu from '../components/RestaurantMenu';
import UserDetails from '../components/UserDetails';
import OrderConfirmation from '../components/OrderConfirmation';
import CustomerAccount from '../components/CustomerAccount';
import Logout from '../components/sections/Logout';


export default () => {
    return (
        <BrowserRouter>
            <Route render={({location}) => (
                <TransitionGroup>
                    <CSSTransition key={location.key} timeout={300} classNames="fade">
                        <Switch location={location}>
                            <Route path="/" component={HomePage} exact={true}/>
                            <Route path="/about" component={AboutUs} />
                            <Route path="/contact" component={ContactUs} />
                            <Route path="/restaurants" component={RestaurantList} />
                            <Route path="/select-menu" component={RestaurantMenu} />
                            <PrivateRoute path="/user-details" component={UserDetails} />
                            <PrivateRoute path="/order-confirmation" component={OrderConfirmation} />
                            <PrivateRoute path="/my-account" component={CustomerAccount} />
                            <PrivateRoute path="/logout" component={Logout} />

                            <Route component={PageNotFound} />
                        </Switch>
                    </CSSTransition>
                </TransitionGroup>
            )} />

        </BrowserRouter>
    );
}
like image 668
Vishal Shetty Avatar asked Jan 27 '23 21:01

Vishal Shetty


1 Answers

In general, converting a stateless functional component (SFC) to a Component is done like this:

  1. Create the class shell for it.

  2. Copy the SFC's body to the render method. If the SFC was an arrow function, add a return as necessary to render.

  3. Change any references to props in the render method to this.props (or just add const { props } = this; at the top). SFCs receive their props in their arguments, but a component receives them as arguments to its constructor; the default constructor will save them as this.props.

    In your case, it's using destructuring on its arguments, so you could do the same with this.props on the right-hand side of the destructuring:

     const { component: Component, ...rest } = this.props;
    

That's it. In your code, you've added parameters to the render function, but it doesn't get called with any arguments, and you've only changed props to this.props a bit haphazardly (including changing auth.isAuthenticated to this.props.customer.isAuthenticated for some reason).

So applying 1-3 above:

// #1 - the shell
class PrivateRoute extends React.Component {
  // #2 - `render`, with the body of the SFC inside
  render() {
    // #3 - destructure `this.props`
    const { component: Component, ...rest } = this.props;
    // #2 (part 2) - add `return`
    return <Route
      {...rest}
      render={props =>
        auth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Component {...props} action="login"/>
        )
      }
    />;
  }
}
like image 71
T.J. Crowder Avatar answered Jan 30 '23 12:01

T.J. Crowder