Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
let UserContext = React.createContext();
class App extends React.Component {
state = {
user: null,
setUser: user => {
this.setState({ user });
}
};
render() {
return (
<UserContext.Provider value={this.state}>
<Router>
<Home path="/" />
<About path="/about" />
<PrivateRoute as={Dashboard} path="/dashboard" />
</Router>
</UserContext.Provider>
);
}
}
class PrivateRoute extends React.Component {
static contextType = UserContext;
render() {
let { as: Comp, ...props } = this.props;
return this.context.user ? <Comp {...props} /> : <Login />;
}
}
class Login extends React.Component {
static contextType = UserContext;
render() {
return (
<form
onSubmit={async () => {
let user = await doWhateverYouNeedToDoToLogin();
this.context.setUser(user);
}}
/>
);
}
}
function Home() {
return <div>home</div>;
}
function About() {
return <div>about</div>;
}
function Dashboard() {
return <div>Protected dashboard</div>;
}
@DevanB

This comment has been minimized.

Copy link

DevanB commented Dec 16, 2018

I'd be curious to see how this works with React Hooks now

@joaodlf

This comment has been minimized.

Copy link

joaodlf commented Sep 9, 2019

+1 for Hooks, but would also love to see how this would work with purely functional components!

@pandaa880

This comment has been minimized.

Copy link

pandaa880 commented Sep 17, 2019

I am having issues with implementing this with nested routes. Can anyone guide me how should i go for nested private routes ? I tried nesting inside the component with router but it just doesn't render the component at all.

@B-R-Bender

This comment has been minimized.

Copy link

B-R-Bender commented Oct 15, 2019

It won't work if we try to nest some component inside of protected component:

    function Dashboard() {
        return (
            <div>
                <Link to={"/home"}>GoHome</Link>
                Protected nested dashboard route won't work - Home component renders anyway
                <Home path={"/home"} />
            </div>
        );
    }

is there a way to get this code works?

@jeud

This comment has been minimized.

Copy link

jeud commented Jan 3, 2020

Hi

class PrivateRoute extends React.Component {
  static contextType = UserContext;

  render() {
    let { as: Comp, ...props } = this.props;
    return this.context.user ? <Comp {...props} /> : <Login />;
  }
}

to me, returning <Login /> doesn't change the URL on the browser, unfortunately the <Redirect /> of the @reach/router serves different purpose from the react-router's

please guide
thanks

@azicchetti

This comment has been minimized.

Copy link

azicchetti commented Feb 3, 2020

Instead of writing a custom component, I prefer to wrap the ones requiring authentication with a HOC.
This has been tested with purely functional components and hooks.

I'm using a store with MobX but this can be easily rewritten using just props.
I decided to use props.navigate instead of <Redirect /> because I can put the original url in location.state.from.
Upon a successful login, I can redirect the user back to the requested path.

const requiresAuthentication = (WrappedComponent) => {
        return observer(
                (props) => {
                        const store = useContext(StoreContext);

                        useEffect( () => {
                                if (!store.isAuthenticated) {
                                        //console.log('not authenticated');
                                        props.navigate('/login', { state: { from: props.uri } });
                                }
                        });

                        return (
                        <React.Fragment>
                        { store.isAuthenticated &&
                        <WrappedComponent {...props} />
                        }
                        </React.Fragment>
                        );
                }
        );
}

export default requiresAuthentication;

Usage (remove the observer wrapper if you're not using MobX):

const Products = requiresAuthentication( observer( (props) => { ... my functional component }) );

@christensen143

This comment has been minimized.

Copy link

christensen143 commented Feb 8, 2020

@DevanB - I think this is what you were curious about.

// AuthContext

export const AuthContext = React.createContext();

export const AuthProvider = AuthContext.Provider;
export const AuthConsumer = AuthContext.Consumer;


// App.js

const App = () => {
  const [user, setUser] = useState(null);
  
  const updateUser = newUser => {
    setUser(newUser);
  }
  
  return (
    <AuthProvider
      value={{
        user,
        updateUser
      }}
    >
      <Router>
        <Home path="/" />
	<About path="/about" />
	<PrivateRoute as={Dashboard} path="/dashboard" />
      </Router>
    </AuthProvider>
  );
}


// PrivateRoute.js

const PrivateRoute = props => {
  const { user } = useContext(AuthContext);
  
  let { as: Comp, ...props } = props;
  return user ? <Comp {...props} /> : <Login />;
}


// Login.js

const Login = () => {
  const { user, updateUser } = useContext(AuthContext);
  
  return (
    <form
      onSubmit={async () => {
        let newUser = await doWhateverYouNeedToDoToLogin();
	  updateUser(newUser);
      }}
    />
  );
}


// Home.js

const Home = () => {
  return <div>home</div>;
}


// About.js

const About = () => {
  return <div>about</div>;
}


// Dashboard.js

const Dashboard = () => {
  return <div>Protected Dashboard</div>;
}
@toroduque

This comment has been minimized.

Copy link

toroduque commented Feb 26, 2020

I am having issues with implementing this with nested routes. Can anyone guide me how should i go for nested private routes ? I tried nesting inside the component with router but it just doesn't render the component at all.

For nested private routes you can pass the children:

class PrivateRoute extends React.Component {
  static contextType = UserContext;

  render() {
    let { as: Comp, children, ...props } = this.props;
    return this.context.user ? <Comp {...props} >{children}</Comp> : <Login />;
  }
}
@dgtlmonk

This comment has been minimized.

Copy link

dgtlmonk commented Mar 5, 2020

@joaodlf

+1 for Hooks, but would also love to see how this would work with purely functional components!

  const Dashboard = () => <div>dashboard</div>;
  const ProtectedRoute = ({ user }) => {
    return user != null ? (
      <Router>
        <Dashboard path="/"></Dashboard>
      </Router>
    ) : (
      <Login  />
    );
  };

  return (
     <AuthProvider
      value={{
        user,
        updateUser
      }}
    >
      <Router>
        <ProtectedRoute path="/" user={user} />
      </Router>
    </AuthProvider>
  );
}
@cvrajeesh

This comment has been minimized.

Copy link

cvrajeesh commented Mar 28, 2020

this is how I implemented PrivateRoute

const PrivateRoute = props => {
  const { user } = useUserContext();
  let { as: Comp, ...otherProps } = props;

  return user ? (
    <Comp {...otherProps} />
  ) : (
    <Redirect to="/login" replace={true} noThrow={true} />
  );
};

Make sure noThrow props is set to true otherwise you'll get exception when redirecting in development.

@markuckermann

This comment has been minimized.

Copy link

markuckermann commented May 10, 2020

@christensen143 I like your hooks example but think

// PrivateRoute.js

const PrivateRoute = props => {
  const { user } = useContext(AuthContext);
  
  let { as: Comp, ...props } = props;
  return user ? <Comp {...props} /> : <Login />;
}

Should be

// PrivateRoute.js

const PrivateRoute = props => {
  const { user } = useContext(AuthContext);
  
  let { as: Comp, ...restOfTheProps } = props;
  return user ? <Comp {...restOfTheProps } /> : <Login />;
}

to avoid an Identifier 'props' has already been declared error.

@harenadata

This comment has been minimized.

Copy link

harenadata commented May 20, 2020

Make sure noThrow props is set to true otherwise you'll get exception when redirecting in development.

@cvrajeesh you fixed my problem. thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.