Auth0 ProtectedRoute component preventing component from changing with state - javascript

I followed the Auth0 React Authentication guide written here:
https://auth0.com/blog/complete-guide-to-react-user-authentication
And implemented the ProtectedRoute component as outlined in the tutorial:
import React from "react";
import { Route } from "react-router-dom";
import { withAuthenticationRequired } from "#auth0/auth0-react";
import { Loading } from "../components/index";
const ProtectedRoute = ({ component, ...args }) => (
<Route
component={withAuthenticationRequired(component, {
onRedirecting: () => <Loading />,
})}
{...args}
/>
);
export default ProtectedRoute;
But now I am having an issue with the ProtectedRoute component that doesn't exist if I use withAuthenticationRequired directly in the export statement of the component that I am trying to protect. I have a web app that contains routes like the following:
<Router>
{isAuthenticated && <Header />}
<Switch>
<Route exact path='/'>
{isAuthenticated ? <Redirect to="/home" /> : <LandingPage />}
</Route>
<ProtectedRoute path='/home' component={Home}/>
<ProtectedRoute path='/events' component={Events}/>
<ProtectedRoute path='/dates' component={Dates}/>
</Switch>
</Router>
And my Home component contains something like the following:
function Home(){
return <div className="home-page">
<Sidebar />
<ProtectedRoute path={"/home/dogs"} component={Dogs}/>
<ProtectedRoute path={"/home/cats"} component={Cats}/>
</div>
}
export default Home;
The bug also happens when the Home component doesn't use ProtectedRoute like so:
function Home(){
return <div className="home-page">
<Sidebar />
<Route path={"/home/dogs"} component={Dogs}/>
<Route path={"/home/cats"} component={Cats}/>
</div>
}
export default Home;
I can't explain why it happens, but it prevents the state within the Sidebar component from changing the sidebar's appearance and rendered components.
Here is a link to a codesandbox on how the sidebar should work (no auth0).
https://codesandbox.io/s/react-routing-problem-2efic
When using ProtectedRoute as in the code above, the active class on the navbar links changes, but the rest of the content stays the same.
However, if I instead take the ProtectedRoute off of the Home component, but use withAuthenticationRequired on the export of the Home component, like so:
export default withAuthenticationRequired(Home, {
onRedirecting: () => (<div>Redirecting you to the login page...</div>)
});
and
<Route path='/home' component={Home}/> //instead of ProtectedRoute
Then everything works as it should.
My questions are:
Why is the ProtectedRoute component behaving differently from when withAuthenticationRequired is at the export level?
Do I need to protect routes that are nested within a protected route?
Thanks for any help!

Related

Not showing any thing on screen while applying authentication in react.js

I was applying authenication in my project in React.js by using protectes routes. First i was using Redirect component from react-router-dom but then i have found out the changes they made in react-router-dom than i applied the navigate component.
import {BrowserRouter,Routes,Route,Navigate} from 'react-router-dom';
import './App.css';
import Navigation from './components/shared/Navigation/Navigation';
import Authenticate from './pages/Authenticate/Authenticate';
import Home from './pages/Home/Home';
import Login from './pages/Login/Login';
import Register from './pages/Register/Register';
const isAuth = true;
function App() {
return (
<div className="App">
<BrowserRouter>
<Navigation/>
<Routes>
<Route exact path='/' element={<Home/>}></Route>
{/* <Route exact path='/register' element={<Register/>}></Route>
<Route exact path='/login' element={<Login/>}></Route> */}
{/* <Route exact path='/authenticate' element={<Authenticate/>}></Route> */}
<GuestRoute exact path='/authenticate' element={<Authenticate/>}></GuestRoute>
</Routes>
</BrowserRouter>
</div>
);
}
const GuestRoute = ({children,...rest}) =>{
return(
<Route {...rest} render = {({location})=>{
return isAuth ?(
<Navigate to = '/rooms'state = {{from : location}} replace />
)
:(
children
)
}}></Route>
)
}
export default App;
This is my code after using the navigate component my screen not showing any thing there must be some kind of logical error in it. Kindly help me to resolve this error.
your app is showing nothing because the route /rooms does not match any routes in the <Routes /> component

React Router content not load when refresh forward or manually input

I am new to React and getting familiarize with React Router. I divided the application into 2 main route since each use a different style: the Splash path is when the user first enter which contain the Splashscreen, Login and Register page; the Menu path is shown after the user is logined. Currently I am working on the Splash route but stumble on a few problem. The Splashscreen contain 1 button that go to the Login Screen. When I click on the button it worked fined, but if I refresh the login page, go back to the Spashscreen and forward, or manually enter the address, the page turn out blank. I tried using HashRouter and it worked, but some suggested that it should only be used on Dev server and not production. I don't use Webpack so I cannot make historyApiFallback: true and the application is running on localhost.
Below are my current code:
App.js:
import React from 'react';
import './App.css';
import SplashTemplate from './component/Template/SplashTemplate';
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route path="/" exact={true}>
<SplashTemplate/>
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
SplashTemplate.js:
import React from 'react';
import Splash from '../Splash/Splash';
import LoginForm from '../LoginForm/LoginForm';
import RegistrationForm from '../RegistrationForm/RegistrationForm';
import '../Template/SplashTemplate.css';
import { BrowserRouter, Route, Switch } from "react-router-dom";
function SplashTemplate() {
return(
<div className="background">
<BrowserRouter>
<Switch>
<Route exact path="/" component={Splash} />
<Route path="/login" component={LoginForm} />
<Route path="/register" component={RegistrationForm}/>
</Switch>
</BrowserRouter>
</div>
);
}
export default SplashTemplate;
Splash.js:
import React from 'react';
import {
BrowserRouter as Router,
Link
} from "react-router-dom";
function Splash() {
return(
<div className="container">
<Link to="/login">
<button className="splashButton">Login</button>
</Link>
</div>
);
}
export default Splash;
Anyone can suggest a solution? I have try the above mention but still not the answer I am looking for.
Update
Thanks to Danilo Venticinque answer I have reformatted the App.js file to:
function App() {
return (
<Router>
<div className="App">
<Switch>
<SplashTemplate>
<Route exact path="/" component={Splash}/>
<Route path="/login" component={LoginForm}/>
<Route path="/register" component={RegistrationForm}/>
</SplashTemplate>
<MainTemplate>
<Route path="/mainmenu" component={MainMenu}/>
</MainTemplate>
</Switch>
</div>
</Router>
);
}
And changed the SplashTemplate.js to:
class SplashTemplate extends Component {
render(){
return(
<div className="background">{this.props.children}</div>
);
};
}
It seem to work for the Splash path but now the problem show up in the Menu path where if the route is input manually (http://localhost:3000/mainmenu) the template for the Menu did not show up but instead the Splash template did. What am I doing wrong here?
Your problem is in the App.js routing. It's rendering the SplashTemplate component (which contains the other routes) only when the exact path is /. So, if you launch the page with /login, SplashTemplate doesn't get rendered (actually, nothing does) and there's no router to serve your other routes.
I would suggest moving the routes into a single component or changing the App.js routing logic so that it would still display SplashTemplate for /login and other desired routes.
So after digging around and following both React Router V4 Routers with Master Pages / Templates and React Router v5.0 Nested Routes I was able to come up with a solution. First, move all the Routing to a seperate file call Route.js and import it to App.js. Since we need to seperate the two path we will put the routing in 2 different functions and call them in a main function like below:
Route.js
...
function LayoutRoute() {
return (
<Switch>
<Route path="/splash" component={SplashLayout} />
<Route path="/main" component={MainLayout} />
<Route path="/" component={NotFound} />
</Switch>
);
}
function SplashLayout() {
return (
<SplashTemplate>
<Route path="/splash/splash" exact component={Splash} />
<Route path="/splash/login" exact component={LoginForm} />
<Route path="/splash/register" exact component={RegistrationForm} />
<Redirect from="/splash" to="/splash/splash" exact />
<Route />
</SplashTemplate>
);
}
function MainLayout() {
return (
<MainTemplate>
<Route path="/main/menu" exact component={MainMenu} />
<Redirect from="/main" to="/main/menu" exact />
<Route />
</MainTemplate>
);
}
const NotFound = () => <h1>Not Found</h1>;
export default LayoutRoute;
The App.js can just call Route:
function App() {
return (
<Router>
<LayoutRoute></LayoutRoute>
</Router>
);
}
And the Template file:
SplashTemplate.js
class SplashTemplate extends Component {
render(){
return(
<div className="background">{this.props.children}</div>
);
};
}
This can help achive seperated Layout for each path of the website/app needed and make sure the page is rendered when entered manually or refresh.

Login with navigation in other component

I am creating a workarea with a side navigation and a header bar. But if the user is not logged in, I want to direct them to the login page.
The main component is as follows
class App extends Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<div>
<Route exact path="/login" component={Login}/>
<Route exact path="/" component={Reporter}/>
</div>
</Router>
</Provider>
);
}
}
export default App;
The Reporter component looks like this:
class Reporter extends Component {
render() {
return (
<Router history={history}>
<div className="reporter-container">
<SideNav/>
<Switch>
<Route exact path="/reporter" component={WorkArea}/>
<Route exact path="/reporter/page1" component={Page1}/>
<Route exact path="/reporter/page2" component={Page2}/>
<Route exact path="/" component={WorkArea}/>
</Switch>
</div>
</Router>
);
}
}
export default Reporter;
In both examples, I have included react-router-dom and history:
import { Switch, Route, Router } from 'react-router-dom'
import history from '../utils/history'
SideNav looks like this:
class SideNav extends Component {
render() {
return (
<div className="side-nav">
<div className="side-nav-item">
<i className="fas fa-file side-nav-icon"></i><Link to="/reporter/page1">Page1</Link>
</div>
<div className="side-nav-item">
<i className="fas fa-male side-nav-icon"></i><Link to="/reporter/page2">Page2</Link>
</div>
</div>
);
}
}
export default SideNav;
The login works fine, but the when I select a side navigation, the URL is going to /reporter/page1, but it is just bank. I think this is because it is looking in the App component routing and finding nothing. I suppose I could move everything to the App component and set the visibility of the side nav bar to none. Does anyone know of the best practice for this pattern?
You shouldn't have two routers, the best practice is to handle all of your routing in a single place. Your code is not working because the router can't find the appropriate component to render for the `reporter/page1' route because it's looking in the first router it finds.
You need to handle all of your routing in a single place. This will also allow you to easily handle the redirection to the /login route with the <Redirect /> component from react-router-dom
You can do it like this
class SomeComponent extends Component {
...
redirectIfNotLoggedIn = () => {
return this.state.isLoggedIn && <Redirect to={'/login'} />
}
render() {
return (
<div>
{this.redirectIfNotLoggedIn()}
<-- Actual component code -->
</div>
)
}
}

Why cant I separate <Switch> to a different module?

Goal
I'm trying to extract the <Switch> with its <Route>'s out to a different module.
Problem
The url is being changed to the new path, but the content doesn't (only when I refresh it changes).
I'm trying to understand what am I missing.
EDITED:
live example: https://stackblitz.com/edit/separated-switch-module
working example:
<BrowserRouter>
<div>
<Link to="/"> Home </Link>
<Link to="contacts"> Contacts </Link>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/contacts" component={Contacts} />
</Switch>
</div>
</BrowserRouter>
failing exmaple:
<BrowserRouter>
<div>
<Link to="/"> Home </Link>
<Link to="contacts"> Contacts </Link>
<SwitchedRoutes/>
</div>
</BrowserRouter>
EDITED:
SwitchedRoutes:
import React from "react";
import { observer, inject } from "mobx-react";
import { Switch, Route } from "react-router-dom";
#inject('pageStore')
#observer
export default class extends React.Component {
render(){
const {
home,
contacts
} = this.props.pageStore.pages;
return (
<Switch>
<Route exact path={home.path} render={()=> <Home />} />
<Route path={contacts.path} render={()=> <Contacts/>} />
</Switch>
)
}
}
Since react-router v4 changed an API a bit you need to give to the all underlying components such as Switch, Link and etc a router context. (Something like subscriber to the routing stuff), as soon as you disconnects the module to the separate file it loses the context.
just add this to the SwitchedRoutes.js
import React from 'react';
import { withRouter } from 'react-router'
import {Switch, Route} from 'react-router-dom';
import {inject, observer} from 'mobx-react';
const Home = () => <h1>Home</h1>;
const Contacts = () => <h1>Contents</h1>;
const switchedRouter = inject('store')(observer(props => {
const {home, contacts} = props.store.routes;
return(
<Switch>
<Route exact path={home.path} component={Home}/>
<Route path={contacts.path} component={Contacts}/>
</Switch>
);
}));
export default withRouter(switchedRouter)
we simply wrapped the component with withRouter HoC which provides us a correct react-router context.
https://separated-switch-module-j92psu.stackblitz.io

Problems with routes with react router and radium

I'm working on a website on reactJS. I use radium and react router for the routes.
I have a lot of problems with routes...
On my main page there is a fixed nav bar menu with a link to the documentation page.
On this documentation page I also have this bar but to access to other links I have to click 2 times to get there..
class App extends Component {
render() {
return (
<Router history={hashHistory}>
<Switch>
<Route exact path="/" component={LandingPage}/>
<Route path="/documentation" component={DocumentationRoutes}/>
<Route path="/blog" component={OnContrustion}/>
<Route path="/contactus" component={OnContrustion}/>
</Switch>
</Router>
);
}
}
export default App;
Here is the DocumentationRoutes:
class DocumentationRoutes extends Component {
render() {
return (
<Router history={hashHistory}>
<Switch>
<Route path="/documentation" component={Documentation}/>
<IndexRoute component={Documentation} />
</Switch>
</Router>
);
}
}
export default DocumentationRoutes;
and the documentation :
class Documentation extends Component {
render() {
return (
<VerticalLayout>
<StretchLayout>
<NavBar />
</StretchLayout>
<StretchLayout margin="20">
<CenterLayout>
<SubTitle>Documentation</SubTitle>
</CenterLayout>
<DocMenu />
</StretchLayout>
</VerticalLayout>
);
}
}
export default Documentation;
Is it the right way to use react router ?
What can I do to be redirected after only one click ?
On the first click, the url change correctly but not the page.
Thanks,
You only need to use the Router component in the initial routes definition. Your DocumentationRoutes component should be:
Also in react-router v4 IndexRoute no longer exists.
class DocumentationRoutes extends Component {
render() {
return (
<Switch>
<Route path="/documentation" component={Documentation}/>
<Route component={Documentation} />
</Switch>
);
}
}
export default DocumentationRoutes;

Categories