Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auth Protected Routes React Router V4 - Passing Props

I have created a reactJS app using create-react-app using Flux as an architecture where I want to have some routes accessible without being authenticated and some only accessible while authenticated. Using the flux design pattern I am passing the state of the store down through each component using props so the store state is available for all child components that require it.

I have studied the documentation here (the example is also pasted below) to try and understand how to achieve the above outcome in my app.

I can't see how I can adapt the example to pass state down to the component called within the protected route without doing this with an explicit name like how component is passed.I want to acheive...

  1. Pass the component to PrivateRoute so it can be called if my user is authenticated.
  2. Pass all props from the parent component to the component called by PrivateRoute (this is to allow me to keep cascading the store state down through the props and also to check in the store state if the user is logged in).

I think I am perhaps misunderstanding something fundamental here. Can anyone advise please?

  import React from "react";
  import {
  BrowserRouter as Router,
  Route,
  Link,
  Redirect,
  withRouter
} from "react-router-dom";

////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time

const AuthExample = () => (
  <Router>
    <div>
      <AuthButton />
      <ul>
        <li>
          <Link to="/public">Public Page</Link>
        </li>
        <li>
          <Link to="/protected">Protected Page</Link>
        </li>
      </ul>
      <Route path="/public" component={Public} />
      <Route path="/login" component={Login} />
      <PrivateRoute path="/protected" component={Protected} />
    </div>
  </Router>
);

const fakeAuth = {
  isAuthenticated: false,
  authenticate(cb) {
    this.isAuthenticated = true;
    setTimeout(cb, 100); // fake async
  },
  signout(cb) {
    this.isAuthenticated = false;
    setTimeout(cb, 100);
  }
};

const AuthButton = withRouter(
  ({ history }) =>
    fakeAuth.isAuthenticated ? (
      <p>
        Welcome!{" "}
        <button
          onClick={() => {
            fakeAuth.signout(() => history.push("/"));
          }}
        >
          Sign out
        </button>
      </p>
    ) : (
      <p>You are not logged in.</p>
    )
);

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

const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;

class Login extends React.Component {
  state = {
    redirectToReferrer: false
  };

  login = () => {
    fakeAuth.authenticate(() => {
      this.setState({ redirectToReferrer: true });
    });
  };

  render() {
    const { from } = this.props.location.state || { from: { pathname: "/" } };
    const { redirectToReferrer } = this.state;

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

    return (
      <div>
        <p>You must log in to view the page at {from.pathname}</p>
        <button onClick={this.login}>Log in</button>
      </div>
    );
  }
}

export default AuthExample;

Here is a section from my actual code...

class Page extends React.Component{
  // constructor(props) {
  //   super(props);
  //   }

  render(){
    return(
    <Router>
      <div>
        <Route exact path="/" render={()=><HomePage {...this.props}/>}/>
        <Route path="/login" render={()=><LoginPage {...this.props}/>}/>
        <PrivateRoute exact path="/protected" component={Main} extra="Boo!"/>
      </div>
    </Router>);
  }
}

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={(props) =>
      (console.log(this.props.extra) || 1) ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

This.props.extra is undefined.

like image 495
Rob Avatar asked Mar 19 '18 13:03

Rob


1 Answers

If you are looking for a way to pass extra props to the PrivateRoute you can do:

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={ (props) =>
      ( console.log(props.extra) || 1) ? (
        <Component {...props} {...rest} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

then

<PrivateRoute exact path="/protected" component={Main} extra="Boo!"/>

and Main should now receive extra prop (together with path and exact).

like image 146
Tomasz Mularczyk Avatar answered Sep 27 '22 19:09

Tomasz Mularczyk