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>; | |
} |
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 />;
}
}
+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>
);
}
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.
@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.
Make sure noThrow props is set to true otherwise you'll get exception when redirecting in development.
@cvrajeesh you fixed my problem. thank you!
For the people that show up here for how to do this with nested routing, I solved it using nested routers:
// App.js
<Router>
<Login path="/login" />
<ForgotPass path="/forgot-password" />
<PrivateRoute as={Dashboard} path="/dashboard/*" />
</Router>
// Dashboard
export default ({children}) => (
<Router>
<DashHome path="/" />
<DashStatistics path="/statistics" />
</Router>
)
New to this. My <Router>
moans that I don't have path
as a prop in the children components I pass. What happens when you have dynamically created pages (like blog posts) in your gatsby-node.js
? Can't I just have...
<Router>
{authenticated && <PrivateRoute/>}
{!authenticated && <PublicRoute/>}
</Router>
...and then not have to think about it further down the component tree? Rather than pepper my whole application with <Component path="path/to/something"/>
?
Also, does <Router/>
effectively stop Gatsby from serving static files?
@DevanB - I think this is what you were curious about.