I am building a React app that has a static marketing site and a dynamic app. I am using a combination of React Router and hooks to separate the two and ensure proper routing throughout.
To begin with, I want users who are logged in to be taken directly to the app when they hit the root ("/") and to the static/marketing site when not logged in. The main marketing site home page has a nav bar that allows users to access other routes such as "/about", whereas the app has a separate nav bar for app navigation.
The problem is, while the authentication based routing for the root route seems to be working, and I can navigate to other routes specified in my top-level file, the routes that are included within my static/marketing site are not accessible.
Top-level/index.js
const routing = (
<Provider store={store}>
<Router>
<NavWrapper />
{/* <Switch> */}
<Route exact path="/" component={AuthWrapper} />
<Route path="/signup" component={SignUp} />
<Route path="/login" component={Login} />
{/* </Switch> */}
</Router>
</Provider>
)
ReactDOM.render(routing, document.getElementById('root'));
AuthWrapper.js (here useAuth() is a React hook that evaluates global Redux isAuthenticated state)
const AuthWrapper = (props) => {
return useAuth() ? <App /> : <Website />
}
export default AuthWrapper;
website/index.js
export const Website = () => {
return (
<>
{/* <SiteNav /> */}
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/about" component={AboutPage} />
<Route path="/how-it-works" component={HowItWorksPage} />
<Route path="/plans" component={PlansPage} />
<Route path="/press" component={PressPage} />
<Route path="/faq" component={FAQPage} />
<Route path="/legal" component={LegalPage} />
<Route path="/for-dieticians" component={DietitiansPage} />
<Route path="/for-trainers" component={PTsPage} />
</Switch>
</>
)
};
In my app, when I enter localhost:3000/ (unauthenticated) I am correctly taken to my home/landing page. However, if I try to use the navigation (or type in any subroutes) to access "/about" for example, I get a blank screen (other than the nav bar). In the React dev tools, the component isn't even rendering.
I think this is the issue:
<Route exact path="/" component={AuthWrapper} />
You want Authwrapper to be shown for about as well, right? But the above only matches "/" exactly.
You should enable the switch statement, but move the AuthWrapper Route to the bottom and then let it have this path: "/:rest*" (maybe "/*" works too, haven't used react-router in a while).
That way, it will use the Route if the first 2 don't match.
Related
I build a website in react with Browserrouting and have the following code & issue
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App/>}>
<Route path="/home" element={<Homepage />} />
The Problem I have is when i run the App it opens the "App" and not the actual Homepage.
I tried to fix it by changing change the App to Homepage but it is not working.
In my understanding / needs to be there so the App displays in any other Path and is functioning.
Can some explain me, how i can make the path / still the Homepage without having to type /home to get there?
Thank you !
It appears you are rendering the Homepage in a nested route that is rendering App. In this configuration <Route path="/" element={<App />}> is what is called a Layout Route. App will be rendered when the path is exactly "/" and should render an Outlet component for the nested routes to render their content to.
An issue I see here, and you should have been seeing a react-router-dom invariant error regarding nesting an absolute path within a route rendering on an absolute path. In other words, "/home" is an absolute path and can't be nested under the absolute path "/"; it's not reachable.
If App really is a layout route and you always want it to render then you could remove the path prop from the parent route rendering App, and move Homepage to "/".
<BrowserRouter>
<Routes>
<Route element={<App />}>
<Route path="/" element={<Homepage />} />
</Route>
</Routes>
</BowserRouter>
or convert Homepage into an index route and leave the "/" path on the parent layout route so the relative routing can still work properly and Homepage is rendered on "/".
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Homepage />} />
</Route>
</Routes>
</BowserRouter>
I should point out that this is a slightly abnormal configuration. Typically the App component would render the Routes and Route components instead of an Outlet and it's assumed to all render on "/" by default. The Homepage component would then be rendered on path="/".
Example:
<BrowserRouter>
<App />
</BowserRouter>
...
const App = () => {
.... app logic ....
return (
<Routes>
<Route path="/" element={<Homepage />} />
</Routes>
);
};
There is OFC, the chance here that you didn't intend to nest these routes and want them to be matched and rendered independently. In this case the solution is to just unnest the Homepage route and continue using absolute paths for the root routes.
Example:
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/home" element={<Homepage />} />
</Routes>
</BowserRouter>
I found the issue
I had to
<Route path="" element={<App/>}>
<Route path="/" element={<Homepage />} />
Delete the Path / in App and only add Path / in Homepage.
It works now
I am facing little bit difficulty, I have to manage my dashboard using material-UI with other components
the process of application is that first I want to open login foam and then the dashboard and I am willing to change just the right-center part of the dashboard with the different component by selecting the listItem option
here is my Dashboard
here is all routes defined on my App.js
<Provider store={store}>
<BrowserRouter>
< Routes>
<Route exact path='/' element={<Login/>} />
<Route exact path='/dashboard' element={<Dashboard/>} />
<Route exact path='/product' element={<Product/>}/>
</Routes>
</BrowserRouter>
</Provider>
currently, the issue is that when I am going to login which is on my "/" route and after the authentication the app navigate on the dashboard but when I click my product tab so the dashboard is disappear there
here is my product page
i am willing to that all pages should to render just in text area where text is showing
I will make some assumptions before answering your question:
You want the Dashboard view to be always available
The Dashboard view's white screen should receive different content depending on the route that you're currently in
In your app you should have some sort of Layout which wraps the entire App. You are using react-router v6 so most probably the way to go it will be this:
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route exact path='/login' element={<Login/>} />
<Route exact path='/dashboard' element={<Dashboard/>} />
<Route exact path='/product' element={<Product/>}/>
</Route>
</Routes>
</BrowserRouter>
</Provider>
And in your Layout component, something like this:
import { Outlet } from 'react-router-dom';
const Layout = () => (
<main>
<Outlet />
</main>
);
You can check the react-router documentation for <Outlet />
Did it help?
EDIT: Forgot to say that yout <Layout /> view should have the Drawer that you're showing in your first screenshot, and the white space is where Outlet should be.
I have a ReactJS app where I have a login screen and when I login successfully, I want to redirect it to my main page but for some reason, I can't get this to work.
I'm using the react-router-dom library.
I'm initializing my app in the index.html using the following:
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
and in my app.jsx, my render function uses the following code:
class App extends Component {
state = {};
render() {
const isAuthenticated = IsAuthenticated();
return (
<AuthContext.Provider value={isAuthenticated}>
<main className="container">
{isAuthenticated ? <NavBar /> : null}
<Switch>
<PrivateRoute path="/home" component={Home} />
{isAuthenticated ? (
<Redirect to="/members" />
) : (
<Route path="/login" component={LoginForm} />
)}
<PrivateRoute path="/" exact component={Home} />
</Switch>
</main>
</AuthContext.Provider>
);
}
}
Note that I've simplified the above code as I have many other pages defined in there but this is the core of it anyway.
I've based my code on an article that I found:
Building Basic React Authentication
Note that I haven't followed all the steps in this article as I've adjusted based on my requirements.
This is the code I'm using when click my login button and I successfully login:
var result = await httpService.post(loginEndPoint, loginCredentials);
const user = result.data;
localStorage.setItem("user", JSON.stringify(user));
this.props.history.replace("/home");
this.props.history.push("/home");
I've left both the history.replace as I wanted to remove my "login" page once I successfully logged in and wanted to make sure I would not be able to come back to the login page until my token expired.
Then I try to use the history.push to go from my login page to my home page but it is somehow remaining on my login page but once I refresh the app in the browser via F5, it does redirect to the home page as my token is valid.
Any ideas why the this.props.history.push is not "redirecting" to my home page?
Let me know if I need to provide any additional code or information.
Thanks.
Update-1:
I don't think the problem is with .push or redirect as I accidently used a page that did not exists in my route after logging in and it redirected to the NotFound route which means routing to another page is working.
This is my full route definition:
<AuthContext.Provider value={isAuthenticated}>
<main className="container">
<ToastContainer />
{isAuthenticated ? <NavBar /> : null}
<Switch>
<PrivateRoute path="/home" component={Home} />
<PrivateRoute path="/admin" component={Admin} />
<Route path="/applicationform" component={ApplicationForm} />
{isAuthenticated ? (
<Redirect to="/members" />
) : (
<Route path="/login" component={LoginForm} />
)}
<Route path="/not-found" component={NotFound}></Route>
<PrivateRoute path="/" exact component={Home} />
<Redirect to="not-found" />
</Switch>
</main>
</AuthContext.Provider>
After logging in, I'm suppose to redirect to the 'home' page and I also have an path set up as "/" which should re-direct me to home but only if logged in successfully.
Can you spot anything defined incorrectly in the above?
In a React SPA, I have a collection of "pages" under the /src/pages/ folder.
The entry point page is an index.js file under the /src/ folder, where I define a router const like this:
const routing = (
<Router>
<div>
<Switch>
<Route path="/signIn" component={SignIn} />
<Route exact path="/" component={Homepage} />
<Route path="/page1" component={Page1} />
<Route path="/page2" component={Page2} />
<Route path="/page3" component={Page3} />
<Route component={NotFound} />
</Switch>
</div>
</Router>
It works great and all. All pages are navigable like "https://mysuperapp.com/page2" and it will render the Page2 React component.
Concerns arise when I incorporate user session management (log in, log out). If a user is not logged in the app, all pages should automatically redirect to the /signIn page. And viceversa, if a user is already logged, if the /signIn page is accessed, it should automatically redirect to the root homepage.
Right now I have implemented this by adding the following code to all the pages, right after the render() method is declared in the component, like this:
class Page2 extends React.Component {
render() {
if (UserProfile.getUserSessionStatus() !== "logged") {
this.props.history.push("/signIn");
}
}
return (
JSX code to be rendered here...
...
This works, but it feels like a cheap workaround used by someone who is just learning React, not by a professional.
For a proof of concept it works, but I would never dare to use such a thing in a production environment.
So, what's the right, best-practices-aligned way to accomplish this?
One possible approach is to create a Higher Order component(HOC) and use it for protecting any routes that require login.
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
UserProfile.getUserSessionStatus() === "logged"
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
And then use like this
.....
<PrivateRoute path='/page1' component={Page1} />
.......
Hope this helps!
I have a problem with my react components.
Basically my interface should be an SPA, built with ReactJS.
During the implementation of the authentication with auth0-js I also implemented some routes.
The layout looks like this:
Whenever I click now on the "Recipes" link, it should redirect to the route "/recipes".
However, when I implement the route with <Route path="/recipes" component={Recipes} /> it only renders what is actually returned in the Recipes component. I know that this is correct from what react does.
Now, I want to keep the navigation bar but only want to exchange the component below, so I want to change what is below the navigation bar like I did in App.js.
How can I achieve this? Is something about the routes, or components not correct?
I do not want to re-render always everything. I also want to keep the style of the whole page. Is there a way to do this?
The whole code can be found here.
I agree with Jyutzio in that you need to move the Navigation component above the child routes to only change to content of the child routes.
In order to have the Navigation bar update with logged in / logged out state you may want to consider implementing redux. I have a project with nearly the exact requirements as yours - a navigation header that is static.
In my header I have import { connect } from 'react-redux';
At the bottom of the component I use connect before exporting:
function mapStateToProps(state) {
return { authenticated: state.auth.authenticated };
}
Header = connect(mapStateToProps)(Header);
export default Header;
Then this allows me to check the "authenticated" piece of state and render accordingly.
renderLogoutButton() {
if(this.props.authenticated) {
return(
<li><a onClick={...}>Logout</a></li>
);
} else {
return(
<li><a onClick={...}>Login</a></li>
);
}
}
You will need to setup a reducer, but there are many resources explaining redux setup.
The router (simplified) I have set up as follows:
import Admin from './index';
...
<BrowserRouter>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/" component={Admin} />
</Switch>
</BrowserRouter>
index:
import AdminRouter from './admin/admin_router';
...
<div>
<Menu />
<div>
<AdminRouter />
</div>
</div>
admin_router:
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/details" component={AdminDetails} />
<Route component={PageNotFound} />
</Switch>
</div>
If you place your Navigation component above the switch, it will solve your problem.
const Routes = () => (
<Router history={history} component={Home}>
<Route component={Navigation}/>
<Switch>
<Route exact path="/" render={props => <Home auth={auth} {...props} />} />
<Route path="/home" render={props => <Home auth={auth} {...props} />} />
<Route
path="/callback"
render={props => {
handleAuthentication(props);
return <Callback {...props} />;
}}
/>
<Route component={NotFound} />
</Switch>
</Router>
);