I'm trying to set routes for my application and i have problems at the beginning..
I would achieve that when user is not logged in, application should redirect him to '/signIn' page.
If user is logged in, application should take him into '/'
I don't understand it but my currentUser is null. So app should redirect me to signIn (condition) but it still goes to HomePage
Any hints?
Below is my code what i've been trying so far
class App extends React.Component {
constructor() {
super();
this.state = {
currentUser: null,
};
}
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(user => {
this.setState({ currentUser: user });
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
console.log('Current state:', this.state);
return (
<div className="App">
<Switch>
<Route exact path="/" render={() => <HomePage currentUser={this.state.currentUser} />} />
<Route
exact
path="/signIn"
render={() => (this.state.currentUser ? <Redirect to="/" /> : <SignInPage />)}
/>
</Switch>
</div>
);
}
}
You should add verification user in route home page not login page:
class App extends React.Component {
constructor() {
super()
this.state = {
currentUser: null
}
}
unsubscribeFromAuth = null
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(user => {
this.setState({ currentUser: user })
})
}
componentWillUnmount() {
this.unsubscribeFromAuth()
}
render() {
const redirection = this.state.currentUser ? <Redirect to="/" /> : <Redirect to="/signIn" />
return (
<div className="App">
{redirection}
<Switch>
<Route
exact
path="/"
render={() => (<HomePage currentUser=
{this.state.currentUser} />)}
/>
<Route exact path="/signIn" component={SignInPage} />
</Switch>
</div>
)
}
}
Related
In my ReactJS app, routes are configured in below way:
class App extends React.Component{
constructor(props){
super(props);
this.state={
isLoggedin: false
}
}
componentDidMount(){
if(localStorage.getItem('name'))
{
this.setState({
isLoggedin: true
})}
}
render(){
return(
<>
<Switch>
<Route exact path="/" render={()=><Login isLoggedin={this.state.isLoggedin} />} />
<Route exact path="/login" render={<Login isLoggedin={this.state.isLoggedin} />} />
<Route exact path="/home" render={()=><Home isLoggedin={this.state.isLoggedin} />} />
</Switch></>
);
}
}
In Login.js:
class Login extends React.Component{
render(){
if(this.props.isLoggedin) return <Redirect to="/home" />;
return(
<h1>Login here</h1>
);
}
}
In Home.js:
class Home extends React.Component{
render(){
if(!this.props.isLoggedin) return <Redirect to="/login" />;
return(
<h1>Home</h1>
);
}
}
So what this code will do is that when the user visits the /, it would first go to Login component and as soon as isLoggedin is set to true, it would redirect the user to Home.js. Same thing would happen if user is not loggedin and he tries to access /home, he would be redirected to /login. Since I am using local storage, all of this would happen in flash of eye. It is also working just fine.
But I doubt if it is the best method to achieve my goal. I want to know if there is any more advisable method to do this.
Thanks!!
A more advisable method would be to decouple the auth checks from the components and abstract this into custom route components.
PrivateRoute - if the user is authenticated then a regular Route is rendered and the props are passed through, otherwise redirect to the "/login" path for user to authenticate.
const PrivateRoute = ({ isLoggedIn, ...props }) => {
return isLoggedIn ? <Route {...props} /> : <Redirect to="/login" />;
};
AnonymousRoute - Basically the inverse of the private route. If the user is already authenticated then redirect them to the "/home" path, otherwise render a route and pass the route props through.
const AnonymousRoute = ({ isLoggedIn, ...props }) => {
return isLoggedIn ? <Redirect to="/home" /> : <Route {...props} />;
};
From here you render the Login and Home components into their respective custom route components.
<Switch>
<PrivateRoute
isLoggedIn={this.state.isLoggedIn} // *
path="/home"
component={Home}
/>
<AnonymousRoute
isLoggedIn={this.state.isLoggedIn} // *
path={["/login", "/"]}
component={Login}
/>
</Switch>
* NOTE: The isLoggedIn={this.state.isLoggedIn} prop is only required here since the isLoggedIn state resides in the App component. A typical React application would store the auth state in a React Context or in global state like Redux, and thus wouldn't need to be explicitly passed via props, it could be accessed from within the custom route component.
Full sandbox code:
const PrivateRoute = ({ isLoggedIn, ...props }) => {
return isLoggedIn ? <Route {...props} /> : <Redirect to="/login" />;
};
const AnonymousRoute = ({ isLoggedIn, ...props }) => {
return isLoggedIn ? <Redirect to="/home" /> : <Route {...props} />;
};
class Login extends Component {
render() {
return (
<>
<h1>Login here</h1>
<button type="button" onClick={this.props.login}>
Log in
</button>
</>
);
}
}
class Home extends Component {
render() {
return (
<>
<h1>Home</h1>
<button type="button" onClick={this.props.logout}>
Log out
</button>
</>
);
}
}
export default class App extends Component {
state = {
isLoggedIn: false
};
componentDidMount() {
if (localStorage.getItem("isLoggedIn")) {
this.setState({
isLoggedIn: true
});
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.isLoggedIn !== this.state.isLoggedIn) {
localStorage.setItem("isLoggedIn", JSON.stringify(this.state.isLoggedIn));
}
}
logInHandler = () => this.setState({ isLoggedIn: true });
logOutHandler = () => this.setState({ isLoggedIn: false });
render() {
return (
<div className="App">
<div>Authenticated: {this.state.isLoggedIn ? "Yes" : "No"}</div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/home">home</Link>
</li>
<li>
<Link to="/login">log in</Link>
</li>
</ul>
<Switch>
<PrivateRoute
isLoggedIn={this.state.isLoggedIn}
path="/home"
render={() => <Home logout={this.logOutHandler} />}
/>
<AnonymousRoute
isLoggedIn={this.state.isLoggedIn}
path={["/login", "/"]}
render={() => <Login login={this.logInHandler} />}
/>
</Switch>
</div>
);
}
}
I am trying to redirect the user to UserProfile after login.
I can login the user successfully but once logged in I cannot figure out why it doesn't redirect me to the UserProfile page.
Not sure what I am doing wrong. Can anybody have a look at this?
App.js
// ...
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
class App extends Component {
this.state = {
isLoggedIn: null,
};
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) { this.setState({ isLoggedIn: true })}
else { this.setState({ isLoggedIn: false })}
})
}
render() {
return (
<BrowserRouter>
<Switch>
<Route path='/' component={Landing} exact />
<Route path='/login' component={Login} />
<Route path='/user-profile' render={() => (
this.state.isLoggedIn ? <UserProfile /> : <Redirect to='/login' />
)} />
</Switch>
</BrowserRouter>
);
}
}
export default App;
Login.js
// ...
import { withRouter } from 'react-router-dom'
class Login extends Component {
this.state = {
// ...
};
_handleSubmit = (e) => {
e.preventDefault();
firebase.auth().signInWithEmailAndPassword(this.state.email, this.state.password)
.then(() => {
// ...
this.props.history.push('/recipe-form')
}).catch((error) => {
// ...
});
}
render() {
return (
<form onSubmit={(e) => this._handleSubmit(e)}>
<Input //...Email />
<Input //...Password />
<Button text='Login' type='submit' />
</form>
);
}
}
export default withRouter(Login);
The checker you are putting on
<Route path='/user-profile' render={() => ( this.state.isLoggedIn ? <UserProfile /> : <Redirect to='/login' /> )}/>
here says that if user currently on user-profile path and he is not logged in, he should suppose to be re directed, but it is says nothing on user that currently in login path.
You can add same (but opposite) logic to Route login decleration -
<Route path='/login' render={() => ( !this.state.isLoggedIn ? <Login /> : <Redirect to='/user-profile' /> )}/>
or add Redirect at Login component itself after successful login attempt
I am new in React and I was trying to route the authenticated user to the main page. Unfortunately the user remains on the login page, although he logged in successfully. Here the relevant snippet of main.js which handles the routing part.
render(){
const {user} = this.props
{console.log("Logged in user: " + JSON.stringify(this.props.user.email))}
{console.log("Logged in : " + JSON.stringify(this.props.user.loggedIn))}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
this.props.user.loggedIn === true
? <Component {...props} />
: <Redirect to={{
pathname: '/login',
}} />
)} />
)
<BrowserRouter>
<main>
<Button icon='log out' floated='right' color='blue' onClick={()=>this.userLogout(user)}/>
<Switch>
<PrivateRoute exact path='/' component={withRouter(StudentsMain)}/>
<Route exact path='/about' component={About}/>
<Route exact path='/login' component={withRouter(Login)}/>
<PrivateRoute exact path='/settings' component={withRouter(Settings)}/>
<PrivateRoute exact path='/assessment/:id' component={Assessment}/>
</Switch>
</main>
</BrowserRouter>
And here the relevant part of the login component...
onValidSubmit = (formData) => {
console.log("logging in")
this.props.logInUser(formData)
this.setState({loggedIn: true})
};
onFormInfo = () => {
this.setState({
showInfo:true
})
}
.....
render(){
if (this.state.loggedIn === true) {
console.log('Redirecting to Main Page')
return <Redirect to='/' />
}
Perhaps somebody can give me a hint, why the user remains on the login page. If I change the url in the browser manually the routing works as expected. Thanks, Arnold
In general, you should lift the state up so that you'll trigger a re-render in the right place. In this example, you can see the difference between triggering the state in different places.
import React, { Component } from 'react';
import { render } from 'react-dom';
class Child extends React.Component {
state = {
loggedIn: false,
}
toggle = () => {
this.setState({ loggedIn: !this.state.loggedIn })
}
render() {
return <p onClick={this.props.onClick || this.toggle}>I am {this.state.loggedIn ? 'in' : 'out'}</p>
}
}
class App extends Component {
state = {
loggedIn: false,
}
toggle = () => {
this.setState({ loggedIn: !this.state.loggedIn })
}
render() {
return (
<React.Fragment>
<Child onClick={this.toggle} />
<Child />
</React.Fragment>
);
}
}
render(<App />, document.getElementById('root'));
Here is a live example.
Creating context
export default React.createContext({
user:null,
login:()=>{}
})
Context controller
export default class ContextHandler extends Component{
state = {
user: null,
}
logout = () => {
this.setState({
user: null
})
}
login = user => {
this.setState({
user: user
})
};
render(){
return(
<AuthContext.Provider value={{ user: this.state, login: this.login, logout: this.logout }} >
{this.props.children}
</AuthContext.Provider>
)
}
}
App.js
class App extends Component{
render() {
return (
<BrowserRouter>
<div>
<Switch>
<Route path="/" exact component={Header} />
<Route path="/login" exact component={Login} />
<Route path="/profile" exact component={UserProfile} />
<Route path="/inputform" exact component={InputForm} />
</Switch>
</div>
</BrowserRouter>
)
}
}
export default App;
index.js
ReactDOM.render((
<ContextHandler>
<App/>
</ContextHandler>
),
document.getElementById('root'));
registerServiceWorker();
User Profile Page
componentDidMount() {
if (!this.context.user) {
return ( <Redirect to = "/login"/> )
}
else{
this.setState({
loading:true
})
getUserEntries(this.context.user.token,(entries)=>{
if(entries !== null){
this.setState({
userEntries:entries
})
}
})
}
}
render() {
let samples, producers
if(!this.context.user){
return(
<Redirect to="/login"/>
)
}
if (this.state.userEntries) {
samples = (this.buildList(this.state.userEntries.samples))
producers = (this.buildList(this.state.userEntries.producers))
}
Once I log in I'm directed to the user page with the correct context. Once I navigate to another page and try go back to the user profile page the context is lost. and i have to login again??
The only two components consuming the context are the nav bar and the user profile page.
Note: I am using the new 16.6 context API i.e no consumer tags in render.
Hi there I have built a ProtectedRoute component and I wish to include it in my App component. While I see it work for many others, I'm not sure why Im the odd one out. Code Below:
const PrivateRoute = ({ component: Component, path, ...rest }) => {
return (<Route {...rest} render={(props) => (
firebaseApp.auth().onAuthStateChanged(user => {
if (user) {
<Component {...props} />;
} else {
<Redirect
to={{
pathname: "/",
state: {
prevLocation: path,
error: "You need to login first!",
},
}}
/>
}
})
)} />
)
}
class App extends Component {
displayName = App.name
constructor(props) {
super(props);
}
render() {
return (
<Layout>
<Route exact path='/' component={Home} />
<Route path='/signin' component={SignIn} />
<Route path='/signup' component={SignUp} />
<PrivateRoute path='/counter' component={Counter} />
<Route path='/fetchdata' component={FetchData} />
</Layout>
);
}
}
export default App;
The Routes render method is returning a function which it shouldn't
firebaseApp.auth().onAuthStateChanged
In your case you should use a React component and not a functional component.
const withAuth = (Component) => {
class AuthComp extends React.Component {
state={
loading: true,
user: null
}
componentDidMount() {
firebaseApp.auth().onAuthStateChanged(user => {
this.setState({loading: false, user})
});
}
render() {
if(loading) {
return <div>Loading...</div>
}
if(!loading && !user) {
return <Redirect
to={{
pathname: "/",
state: {
prevLocation: path,
error: "You need to login first!",
},
}}
/>
}
return <Component {this.props} />
}
}
}
const PrivateRoute = ({ component: Component, path, ...rest }) => {
return (<Route {...rest} component={withAuth(Component)} />)
}