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>;
}
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.