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.
Related
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>
)
}
}
When I click Logout in the side menu,
It should return to /login, but still remain in the same route /home with content disappearing.
Explanation of a few states in App.js
import React, { Component } from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import { Redirect } from "react-router";
import Login from "./Login";
import Drawer from "./Drawer";
console.warn = console.error = () => {};
class App extends Component {
state = {
isAuthenticated: false,
isAuthenticating: true,
user: null
};
setAuthStatus = (authenticated) => {
this.setState({ isAuthenticated: authenticated });
};
setUser = (user) => {
this.setState({ user: user });
};
handleLogout = async () => {
try {
//Sign Out api runs here
this.setAuthStatus(false);
this.setUser(null);
} catch (error) {
console.log(error);
}
};
async componentDidMount() {
try {
// run api change to auth setAuthStatus
// if api no return error > continue
this.setAuthStatus(true);
let user = { id: 123 };
this.setUser(user);
} catch (error) {
console.log(error);
}
this.setState({ isAuthenticating: false });
}
render() {
const authProps = {
isAuthenticated: this.state.isAuthenticated,
user: this.state.user,
setAuthStatus: this.setAuthStatus,
setUser: this.setUser
};
return (
!this.state.isAuthenticating && (
<React.Fragment>
{this.state.isAuthenticated ? (
<Drawer
props={this.props}
auth={authProps}
handleLogout={this.handleLogout}
/>
) : (
<BrowserRouter>
<Switch>
<Redirect exact from="/" to="/Login" />
<Route
path="/login"
render={(props) => <Login {...props} auth={authProps} />}
/>
</Switch>
</BrowserRouter>
)}
</React.Fragment>
)
);
}
}
export default App;
isAuthenticating: componentDidMount will call api to see if the session continues,
if yes, isAuthenticating equals to true and it can prevent losing user auth in current session when refreshing the page or go to other route.
isAuthenticated:
If !isAuthenticated > must stay in/redirect to Login Page
SandBox:
https://codesandbox.io/s/restless-breeze-p5uih?file=/src/App.js
I works as expected in the sandbox. You hide the drawer and the main content if the user is logged out with the
this.state.isAuthenticated ? (Drawer) : <Login/>
You should seperate the drawer from your main content and merge the routers together. You should only use one router for the app in general. Also remove the redirect:
<React.Fragment>
{this.state.isAuthenticated ? (
<Drawer
props={this.props}
auth={authProps}
handleLogout={this.handleLogout}
/>
) : (
<BrowserRouter>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<Switch>
{Routes.map((route) => {
return (
<Route
path={route.path}
render={(prop) => (
<route.component {...prop} auth={props.auth} />
)}
/>
);
})}
<Route
path="/login"
render={(props) => <Login {...props} auth={authProps} />}
/>
</Switch>
</main>
</BrowserRouter>
)}
</React.Fragment>
To route to the logout, first move the browser router up to index to wrap .
This will let you use the withRouter HigherOrderComponent:
export default withRouter(App)
Now you can access history from the props and move to login:
this.props.history.push("/login")`
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.
I am trying to create a server-side auth guard for react route. My flow is like... Whenever a route is hit on the front-end, a backend call is made to check if the user's session is present in the Database (since we store session in the database to keep track of sessions).
Here is my app component code:
export default function App() {
const routeComponents = routes.map(({ path, component }) => <AuthenticatedRoute exact path={path} component={component} props={'exact'} key={path} />);
return (
<div>
{window.location.origin === constant.PRODUCTION_DOMAIN_NAME && <Route
path="/"
render={({ location }) => {
if (typeof window.ga === 'function') {
window.ga('set', 'page', location.pathname + location.search);
window.ga('send', 'pageview');
}
return null;
}}
/>}
<Switch>
<Route path="/login" component={LoginPage} />
{routeComponents}
</Switch>
<ToastContainer autoClose={constant.TOASTER_FACTOR} closeButton={false} hideProgressBar />
</div>
);
}
App.propTypes = {
component: PropTypes.any,
};
I have segregated the authenticated route into a separate class component
like:
export class AuthenticatedRoute extends React.Component {
componentWillMount() {
//I call backend here to check if user is authenticated by session Id in DB
}
render() {
const { component: Component, ...rest } = this.props;
return (
<Route exact {...rest} render={(props) => this.state.isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />} />
);
}
}
AuthenticatedRoute.propTypes = {
component: PropTypes.any,
....
};
const mapStateToProps = (state) => ({
reducer: state.get('myReducer'),
});
export default connect(mapStateToProps, { func })(AuthenticatedRoute);
But I face an issue where the login page is redirected twice. Could someone let me know a better approach for this?