React Routing not rendering - reactjs

I am using React routing v4 for a application that has a login and a home page once a dumb auth is done.
As of this point I have this LoadComponent.jsx in my index.js file:
class LoadComponent extends Component {
state = {
isLoggedOn: false,
};
onLoginCheck = (name, password) => {
console.log(name, password);
if (name && password) {
setTimeout(name, 100); // fake async
console.log('set the timeout');
}
this.setState({
isLoggedOn: true,
});
};
checkAuth = () => {
const { isLoggedOn } = this.state;
console.log('checking auth: ', isLoggedOn);
return (isLoggedOn);
};
render() {
return (
<BrowserRouter >
<Switch>
<Header isLogged={this.checkAuth()} />
<Route path="/login" render={props => <Login isLoggedOn={this.state.isLoggedOn} onLoggedInCheck={this.onLoginCheck} {...props} />} />
<PrivateRoute path="/" component={App} authenticated={this.state.isLoggedOn} />
</Switch>
</BrowserRouter>
);
}
}
My privateRouter looks like the following :
const PrivateRoute = ({
component, exact = false, path, authenticated,
}) => {
console.log('here : ', authenticated);
return (
<Route
exact={exact}
path={path}
render={props => (
authenticated ? (
React.createElement(component, props)
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location },
}}
/>
)
)}
/>
);
};
export default PrivateRoute;
The only thing that is rendered on the page is the Header component which makes sense, but the PrivateRoute component is not functioning since at first the Login component should be displaying. Im not sure what I am doing wrong here since I have followed the react router Redirect Auth example to some degree.

Related

React App redirects to route "/" when I try to navigate by changing url manually

I am creating a React app, that stores the users role (there are two possible roles 0, and 1 which are being used for conditional rendering) in a "global" React Context. The role gets assigned upon login. I am also using React Router to handle Routing, and wrote a ProtectedRoute component. My Problem is the following: When I am navigating via the NavBar all works perfectly fine, but when I enter e.g. /home into the url I get redirected to the LoginPage(which is the standard route when the role is not set to 0 or 1) and I can not access the other Routes anymore. However, the user does not get logged out (The session in the database is not being deleted), the app just seems to forget the global state "role", which is used to determine whether the user is allowed to access the individual routes. I am afraid my knowledge of the DOM and Router is too limited to solve this problem.
function App() {
return (
<AuthProvider>
<Router>
<NavigationBar />
<AuthContext.Consumer>
{context => (
<React.Fragment>
{!context.isAuthenticated() ? <Jumbotron/> : null}
</React.Fragment>
)}
</AuthContext.Consumer>
<Layout>
<Switch>
<Route exact path="/" component={() => <LandingPage />} />
<ProtectedRoute path="/home" component={Home} />
<ProtectedRoute path="/about" component={About}/>
<ProtectedRoute path="/account" component={Account} />
<ProtectedRoute path="/calender" component={Calender} />
<ProtectedRoute path="/xyz" component={Xyz} />
<ProtectedRoute path="/wasd" component={wasd} role={0} />
<Route component={NoMatch} />
</Switch>
</Layout>
</Router>
</AuthProvider>
);
}
export default App;
export const ProtectedRoute = ({ component: Component, ...rest }) => {
const { isAuthenticated, getRole } = useContext(AuthContext);
if (rest.role === 0) {
return (
<Route
{...rest}
render={props =>
getRole() === 0 ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/404",
state: {
from: props.location
}
}}
/>
)
}
/>
);
} else if (rest.role === 1) {
return (
<Route
{...rest}
render={props =>
getRole() === 1 ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/404",
state: {
from: props.location
}
}}
/>
)
}
/>
);
} else {
return (
<Route
{...rest}
render={props =>
isAuthenticated() ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/",
state: {
from: props.location
}
}}
/>
)
}
/>
);
}
};
import React from "react";
const AuthContext = React.createContext();
export default AuthContext;
class AuthProvider extends Component {
constructor() {
super();
this.state = {
role: 2, //none=2
name: "",
email: ""
};
}
render() {
return (
<AuthContext.Provider
value={{
state: this.state,
isAuthenticated: () => {
if (this.state.role === 1 || this.state.role === 0) {
return true;
}
return false;
},
setRole: newRole =>
this.setState({
role: newRole
}),
getRole: () => {
return this.state.role;
},
setName: newName =>
this.setState({
name: newName
}),
getName: () => {
return this.state.name;
},
setEmail: newEmail =>
this.setState({
email: newEmail
}),
getEmail: () => {
return this.state.email;
},
}}
>
{this.props.children}
</AuthContext.Provider>
);
}
}
export default AuthProvider;
If you are entering the url directing into the browser, React will reload completely and you will lose all state whether 'global' or otherwise. The most likely scenario is that your router is trying to validate your ability to view a component before you have your auth data.
You don't include how you get your auth session from the database, but even if you refetch the auth session, there is going to be a period where your app has mounted, but you don't have the response yet. This will cause your protected route to believe you are unauthorized, and redirect to the fallback path.
Try adding a check either inside your protected route or before the router itself, that blocks rendering until your auth data is loaded. Although upon reading your question again, it seems like you may not be refetching your logged in user at all.

Protected routes in react-redux app, React router is not working as expected

I have passport strategy with express server on backend. I have set up all the passport strategies and authentication. Now I need to protect some of my Routes. I am using redux to store the status whether the user is logged in or not.
I have tried creating a Private route according to this tutorial. But it got into infinite loop (infinite rendering of the component).
Following code successfully connects to redux store , dispatches an action, and backend is also working fine.
But please suggest me why am i into infinate loop.
My Protected function looks like :
const login = {
type: "Login"
};
const logout = {
type: "Logout"
};
const AUTH =
process.env.NODE_ENV === "production"
? "https://birdiez.herokuapp.com/auth/check"
: "http://localhost:3090/auth/check";
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props => {
console.log("calling api");
fetch(AUTH)
.then(response => {
return response.json();
})
.then(data => {
console.log("Promise resolved:", data);
data.msg === "OK" ? store.dispatch(login) : store.dispatch(login);
});
const { status } = store.getState();
return status === true ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
/>
);
}}
/>
);
export default PrivateRoute;
My App.js looks like :
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<PrivateRoute path="/dash" component={Dash} />
<Route exact path="*" component={Home} />
</Switch>
</Router>
);
}
}

Programmatically routing after login in React

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.

React route authentication

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?

Auth Protected Routes React Router V4 - Passing Props

I have created a reactJS app using create-react-app using Flux as an architecture where I want to have some routes accessible without being authenticated and some only accessible while authenticated. Using the flux design pattern I am passing the state of the store down through each component using props so the store state is available for all child components that require it.
I have studied the documentation here (the example is also pasted below) to try and understand how to achieve the above outcome in my app.
I can't see how I can adapt the example to pass state down to the component called within the protected route without doing this with an explicit name like how component is passed.I want to acheive...
Pass the component to PrivateRoute so it can be called if my user is authenticated.
Pass all props from the parent component to the component called by PrivateRoute (this is to allow me to keep cascading the store state down through the props and also to check in the store state if the user is logged in).
I think I am perhaps misunderstanding something fundamental here. Can anyone advise please?
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time
const AuthExample = () => (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" component={Protected} />
</div>
</Router>
);
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
export default AuthExample;
Here is a section from my actual code...
class Page extends React.Component{
// constructor(props) {
// super(props);
// }
render(){
return(
<Router>
<div>
<Route exact path="/" render={()=><HomePage {...this.props}/>}/>
<Route path="/login" render={()=><LoginPage {...this.props}/>}/>
<PrivateRoute exact path="/protected" component={Main} extra="Boo!"/>
</div>
</Router>);
}
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={(props) =>
(console.log(this.props.extra) || 1) ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
This.props.extra is undefined.
If you are looking for a way to pass extra props to the PrivateRoute you can do:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={ (props) =>
( console.log(props.extra) || 1) ? (
<Component {...props} {...rest} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
then
<PrivateRoute exact path="/protected" component={Main} extra="Boo!"/>
and Main should now receive extra prop (together with path and exact).

Resources