React Router Redirect not Rendering - javascript

Hei Guys,
I got a strange problem that is stopping my development progress for days now.
In my React App I'm using React-Router (Browser Router).
Everthing is working fine (Link, Route, Switch) except Redirect.
redirect is changing the URL but not Rendering the 'new' component. E.g. I got a login screen and after Submit I want to redirect to user screen. Url is changing to URL but screen stays empty (The Sidenav is rendered but not the component, that shows the users information).
Does anybody know how to solve this? There are no errors in console.
Body Component:
<Switch>
<Route exact path="/">
<Home />
</Route>
<PrivateRoute path="/user" component = {User} />
<Route path="/login">
<Login/>
</Route>
<Route path="/register">
<Register />
</Route>
<NotFound />
</Switch>
Private Route:
const PrivateRoute = ({ component: Component, user, ...rest }) => {
return (
<Route {...rest} render={
props => {
if (user) {
return <Component {...rest} {...props} />
} else {
return <Redirect to={
{
pathname: '/login',
state: {
from: props.location
}
}
} />
}
}
} />
)
}
export default PrivateRoute;
Return Value of the render method of Login Screen, this part should be rendered when logged in:
return (
<Route render={
props => {
return <Redirect to={
{
pathname: '/user',
state: {
from: this.props.location
}
}
} />
}
}
/>
);
On Reloading everthing workls perfectly.
Using of Private Route also works.
No Typos.
regards
Manuel

You should not return a Route at your render method but rather a Redirect directly.
so sth like
return (
<>
{isLoggedIn ?
<Redirect to={{
pathname: '/user',
state: { from: this.props.location }
}} />
: LOGIN_FORM_HTML
}
</>
)

Related

React: private route not navigating

I am using a private route to navigate to a route after the user has logged in. However, I am facing an issue. I don't know why but my router is not transitioning to the desired route. Here's my code:
Routes.js
...
...
<PrivateRoute
authenticated={localStorage.getItem("isAuthenticated")}
path="/dashboard"
component={DashBoard}
exact
></PrivateRoute>
PrivateRoute.js
const PrivateRoute = ({ component: Component, authenticated, ...rest }) => (
<Route
{...rest}
render={props =>
authenticated ? (
<Component {...rest} {...props} />
) : (
<Redirect
to={{
pathname: '/',
state: { from: props.location }
}}
/>
)
}
/>
);
export default PrivateRoute;
Login.js
localStorage.setItem("isAuthenticated", true);
this.props.history.push('/dashboard');
Any help would be appreciated. Thanks!
So, I found the solution.
authenticated={localStorage.getItem("isAuthenticated")}
the above was invoking the method at application bootstrap due to which I was having the value of null being stored in my authenticated variable so, I changed it to arrow function and passed the argument without invoking it like below:
authenticated={() => localStorage.getItem("isAuthenticated")}
Can you just try it?
PrivateRoute.js
const PrivateRoute = ({ component: Component, authenticated, ...rest }) => {
console.log("authenticated",authenticated)//is it true or false?
if (authenticated=="true")
return (
<Route {...rest}>
{" "}
<Component {...rest} {...props} />
</Route>
);
else
return (
<Redirect
to={{
pathname: "/",
state: { from: props.location },
}}
/>
);
};
export default PrivateRoute;

Rewriting React router v4 class based code to v6 functional based

I'm trying to implement oauh login with react and spring boot and I've found a tutorial I can follow.
The issue I have is that it is using React Router v4, I would like to update it to use React Router v6 and using Functional components instead.
Login.js
import React, { Component } from 'react';
import './Login.css';
import { GOOGLE_AUTH_URL, FACEBOOK_AUTH_URL, GITHUB_AUTH_URL, ACCESS_TOKEN } from '../../constants';
import { login } from '../../util/APIUtils';
import { Link, Redirect } from 'react-router-dom'
import fbLogo from '../../img/fb-logo.png';
import googleLogo from '../../img/google-logo.png';
import githubLogo from '../../img/github-logo.png';
import Alert from 'react-s-alert';
class Login extends Component {
componentDidMount() {
// If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
// Here we display the error and then remove the error query parameter from the location.
if(this.props.location.state && this.props.location.state.error) {
setTimeout(() => {
Alert.error(this.props.location.state.error, {
timeout: 5000
});
this.props.history.replace({
pathname: this.props.location.pathname,
state: {}
});
}, 100);
}
}
render() {
if(this.props.authenticated) {
return <Redirect
to={{
pathname: "/",
state: { from: this.props.location }
}}/>;
}
return (
<div className="login-container">
<div className="login-content">
<h1 className="login-title">Login to SpringSocial</h1>
<SocialLogin />
<div className="or-separator">
<span className="or-text">OR</span>
</div>
<LoginForm {...this.props} />
<span className="signup-link">New user? <Link to="/signup">Sign up!</Link></span>
</div>
</div>
);
}
}
class SocialLogin extends Component {
render() {
return (
<div className="social-login">
<a className="btn btn-block social-btn google" href={GOOGLE_AUTH_URL}>
<img src={googleLogo} alt="Google" /> Log in with Google</a>
<a className="btn btn-block social-btn facebook" href={FACEBOOK_AUTH_URL}>
<img src={fbLogo} alt="Facebook" /> Log in with Facebook</a>
<a className="btn btn-block social-btn github" href={GITHUB_AUTH_URL}>
<img src={githubLogo} alt="Github" /> Log in with Github</a>
</div>
);
}
}
App.js
This is the App.js with the routes, I have updated it to use Functional components and React Router v6.
//imports left out
function App() {
const [globalUserState, setGlobalUserState] = useState({
authenticated: false,
currentUser: null,
loading: true
});
useEffect(() => {
loadCurrentlyLoggedInUser();
})
const loadCurrentlyLoggedInUser = () => {
getCurrentUser()
.then(res => {
setGlobalUserState({
currentUser: res,
authenticated: true,
loading: false
});
}).catch(err => {
setGlobalUserState({
loading: false
})
})
}
const handleLogout = () => {
localStorage.removeItem(ACCESS_TOKEN);
setGlobalUserState({
authenticated: false,
currentUser: null
});
Alert.success("You're safely logged out!");
}
return (
<Router>
<div className="app">
<div className="app-header">
<AppHeader />
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<SecuredRoute> <Profile /> </SecuredRoute>} />
<Route path="/login" element={(props) => <Login authenticated={globalUserState.authenticated} {...props} />} />
<Route path="/signup" element={(props) => <Signup authenticated={globalUserState.authenticated} {...props} />} />
<Route path="/oauth2/redirect" element={<OAuth2RedirectHandler />} />
<Route path="*" element={<Notfound />} />
</Routes>
<Alert stack={{limit: 3}}
timeout = {3000}
position='top-right' effect='slide' offset={65}
/>
</div>
</Router>
);
}
export default App;
What I would like clarity on
I'm struggling to understand the equivalent of the react router functionalities with v6 (location.state.error, history.replace, location.pathname etc) and functional components instead of class based.
Also, If someone can explain this line please
<LoginForm {...this.props} />
Q1
I'm struggling to understand the equivalent of the react router
functionalities with v6 (location.state.error, history.replace,
location.pathname etc) and functional components instead of class
based.
In react-router-dom v6 there are no longer route props, i.e. no history, location, and no match. The Route components also no longer have component or render props that take a reference to a React component or a function that returns JSX, instead they were replaced by the element prop that takes a JSX literal, i.e. ReactElement.
If I'm understanding your question(s) correctly you are asking how to use RRDv6 with the class components Login and Signup.
You've a couple options:
Convert Login and Signup into React function components as well and use the new React hooks.
I won't cover the conversion, but the hooks to use are:
useNavigate - history object was replaced by a navigate function.
const navigate = useNavigate();
...
navigate("....", { state: {}, replace: true });
useLocation
const { pathname, state } = useLocation();
Create a custom withRouter component that can use the hooks and pass them down as props.
const withRouter = WrappedComponent => props => {
const navigate = useNavigate();
const location = useLocation();
// etc... other react-router-dom v6 hooks
return (
<WrappedComponent
{...props}
navigate={navigate}
location={location}
// etc...
/>
);
};
Decorate the Login and Signup exports:
export default withRouter(Login);
Swap from this.props.history.push to this.props.navigate:
componentDidMount() {
// If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
// Here we display the error and then remove the error query parameter from the location.
if (this.props.location.state && this.props.location.state.error) {
setTimeout(() => {
const { pathname, state } = this.props.location;
Alert.error(state.error, { timeout: 5000 });
this.props.navigate(
pathname,
{ state: {}, replace: true }
);
}, 100);
}
}
What remains is to fix the routes in App so they are correctly rendering JSX.
<Router>
<div className="app">
<div className="app-header">
<AppHeader />
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/profile"
element={(
<SecuredRoute>
<Profile />
</SecuredRoute>
)}
/>
<Route
path="/login"
element={<Login authenticated={globalUserState.authenticated} />}
/>
<Route
path="/signup"
element={<Signup authenticated={globalUserState.authenticated} />}
/>
<Route path="/oauth2/redirect" element={<OAuth2RedirectHandler />} />
<Route path="*" element={<Notfound />} />
</Routes>
<Alert stack={{limit: 3}}
timeout = {3000}
position='top-right' effect='slide' offset={65}
/>
</div>
</Router>
Q2
Also, If someone can explain this line please <LoginForm {...this.props} />
This is simply taking all the props that were passed to the parent component and copying/passing along to the LoginForm component.
<LoginForm {...this.props} />
Login is passed an authenticated prop as well as whatever new "route props" were injected, and any other props injected by any other HOCs you may be using, and the above passes them all along to LoginForm.

support render props of react router in route hoc

I am writing a HOC for public route and private route. If the route is private and the user is authenticated then let him/her enter that page else redirect to login component. If the route is public and the user is not authenticated then show the page and also show the login page if the user is not authenticated but user is authenticated and still goes to login page then redirect the user to root page. This is working fine. But if i use the render instead of component, then it does not work. I could make it work only if i pass the component from the props called component of react-router.
How can i make it work if user user render props?
Here is my code
<Switch>
<PrivateRoute
exact
path="/"
render={() => <Home name="something" />} {/* this does not work */}
/>
<PrivateRoute exact path="/demo" component={Demo} />
<PublicRoute restricted={true} path="/auth" component={Authentication} />
</Switch>
PublicRoute.js
const PublicRoute = ({component: Component, restricted, ...rest}) => {
return (
<Route
{...rest}
render={props =>
isLogin() && restricted ? <Redirect to="/" /> : <Component {...props} />
}
/>
)
}
PrivateRoute.js
const PrivateRoute = ({component: Component, ...rest}) => {
return (
<Route
{...rest}
render={props =>
isLogin() ? <Component {...props} /> : <Redirect to="/auth/login" />
}
/>
)
}
Also if there is any additional things to improve, please do suggest me.
The problem is that in your custom routes you are always using the component prop. So when passing the render prop it is overruled by the one in your custom route and thus trying to render the provided component.
When you modify it like the function below, it will work. It also extracts the render prop and if it's a function it will use that instead of the component prop.
const PrivateRoute = ({component: Component, render, ...rest}) => {
const renderContent = props => {
if (!fakeAuth.isAuthenticated) {
return (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
return (typeof render === 'function') ? render(props) : <Component {...props} />
}
return (
<Route {...rest} render={renderContent} />
);
}

Authentication for a reactjs application using two private routes

My reactjs application has two types of Users namely Artist and Lovers. Some of my components are only accessible to artist and some are only accessible to lovers. So i need to implement Artist and User Routes that will help grand access only to the required User type.
And here is my Router Switch
<Switch>
<Route exact path='/' component={Home} />
<UserRoute authed={this.state.lover} path='/user-dash' component={About} />
<ArtistRoute authed={this.state.artist} path='/artist-dash' component={Contact} />
<Route path='/SignupUser' component={SignupUser} />
</Switch>
Here is my UserRoute code
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
export const UserRoute = ({ component: Component, authed, ...rest }) => (
<Route {...rest} render={props => (
authed
? <Component {...props} />
: <Redirect to={{ pathname: '/', state: { from: props.location } }} />
)} />
)
I want to be able to receive the value of authed in the UserRoute passed in the switch. I do not know why authed in the UserRoute always returns false.
even when this.state.lover passed to it is true. Please what am I doing wrong.
Thanks
Route.jsx
<Switch>
<Route exact path='/' component={Home} />
<Route path='/user-dash' component={AuthCheck(About)} /> // Wrap the component with HOC
</Switch>
AuthCheck.jsx
export default function(Component) {
class AuthCheck extends Component {
render() {
if (this.props.auth.payload) {
return <Component {...this.props} /> // Component if auth is true
} else {
return <Route path='*' exact={true} component={NotFound} /> // 404 if not auth
}
}
}
function mapStateToProps(state) {
return { auth: state.auth }
}
return connect(mapStateToProps)(AuthCheck)
}
Check the above example works with redux
Make sure to import AuthCheck in the Route.jsx file

localStorage retuning `undefined` on ReactJS and react-router-v4 code

I´m bulding a simple router to build my authentication mechanism. The mechanism will use JWT stored in localStorage to know I´m authenticated. Here is my code:
import React, { Component } from "react";
import PropTypes from "prop-types";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
}
const isAuthenticated = () => {
let token = localStorage.getItem("jwtToken");
console.log('Local Storage JWT TOKEN:');
console.log(token); // <<<<=== undefined
if (token) {
return true;
}
return false;
};
const AuthenticatedRoute = ({ component: Component, ...rest }) =>
<Route
{...rest}
render={props =>
isAuthenticated()
? <Component {...props} />
: <Redirect
to={{
pathname: "/landing",
state: { from: props.location }
}}
/>}
/>;
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path="/landing" component={Landing} />
<Route exact path="/logout" component={Logout} />
<Route exact path="/pagenotfound" component={PageNotFound} />
<AuthenticatedRoute exact path="/" component={AppNav} />
<AuthenticatedRoute
exact
path="/:option"
component={AppNav}
/>
<Route component={PageNotFound} />
</Switch>
</Router>
);
}
}
export default App;
For some reason localStorage.getItem is returnin undefined if no token is found. I was expecting null here as expected here
Why is my getItem returning undefined and how to solve it?

Categories

Resources